<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#222"><meta name="generator" content="Hexo 6.3.0">

  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">



<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css" integrity="sha256-Z1K5uhUaJXA7Ll0XrZ/0JhX4lAtZFpT6jkKrEDT0drU=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.1.1/animate.min.css" integrity="sha256-PR7ttpcvz8qrF57fur/yAx1qXMFJeJFiA6pSzWi0OIE=" crossorigin="anonymous">

<script class="next-config" data-name="main" type="application/json">{"hostname":"example.com","root":"/","images":"/images","scheme":"Muse","darkmode":false,"version":"8.14.1","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":{"enable":false,"style":null},"bookmark":{"enable":false,"color":"#222","save":"auto"},"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"stickytabs":false,"motion":{"enable":true,"async":false,"transition":{"menu_item":"fadeInDown","post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果：${query}","hits_time":"找到 ${hits} 个搜索结果（用时 ${time} 毫秒）","hits":"找到 ${hits} 个搜索结果"},"path":"/search.xml","localsearch":{"enable":true,"trigger":"auto","top_n_per_article":-1,"unescape":false,"preload":false}}</script><script src="/js/config.js"></script>

    <meta property="og:type" content="website">
<meta property="og:title" content="JsyBlog">
<meta property="og:url" content="http://example.com/page/20/index.html">
<meta property="og:site_name" content="JsyBlog">
<meta property="og:locale" content="zh_CN">
<meta property="article:author" content="SongyangJi">
<meta name="twitter:card" content="summary">


<link rel="canonical" href="http://example.com/page/20/">



<script class="next-config" data-name="page" type="application/json">{"sidebar":"","isHome":true,"isPost":false,"lang":"zh-CN","comments":"","permalink":"","path":"page/20/index.html","title":""}</script>

<script class="next-config" data-name="calendar" type="application/json">""</script>
<title>JsyBlog</title>
  








  <noscript>
    <link rel="stylesheet" href="/css/noscript.css">
  </noscript>
</head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <div class="column">
      <header class="header" itemscope itemtype="http://schema.org/WPHeader"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏" role="button">
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <i class="logo-line"></i>
      <h1 class="site-title">JsyBlog</h1>
      <i class="logo-line"></i>
    </a>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger" aria-label="搜索" role="button">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>



<nav class="site-nav">
  <ul class="main-menu menu"><li class="menu-item menu-item-home"><a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a></li><li class="menu-item menu-item-tags"><a href="/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签</a></li><li class="menu-item menu-item-categories"><a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a></li><li class="menu-item menu-item-archives"><a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a></li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup"><div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off" maxlength="80"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close" role="button">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div class="search-result-container no-result">
  <div class="search-result-icon">
    <i class="fa fa-spinner fa-pulse fa-5x"></i>
  </div>
</div>

    </div>
  </div>

</header>
        
  
  <aside class="sidebar">

    <div class="sidebar-inner sidebar-overview-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">SongyangJi</p>
  <div class="site-description" itemprop="description"></div>
</div>
<div class="site-state-wrap animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
        <a href="/archives/">
          <span class="site-state-item-count">251</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
          <a href="/categories/">
        <span class="site-state-item-count">45</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
          <a href="/tags/">
        <span class="site-state-item-count">109</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>

        </div>
      </div>
    </div>

    
  </aside>


    </div>

    <div class="main-inner index posts-expand">

    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/11/12/Java%E4%B8%AD%E7%9A%84volatile%E5%85%B3%E9%94%AE%E5%AD%97%E3%80%81JMM%E3%80%81happen-before/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/11/12/Java%E4%B8%AD%E7%9A%84volatile%E5%85%B3%E9%94%AE%E5%AD%97%E3%80%81JMM%E3%80%81happen-before/" class="post-title-link" itemprop="url">Java中的volatile关键字、JMM、happen-before</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-11-12 02:00:00" itemprop="dateCreated datePublished" datetime="2021-11-12T02:00:00+08:00">2021-11-12</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2022-09-15 14:04:24" itemprop="dateModified" datetime="2022-09-15T14:04:24+08:00">2022-09-15</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/Java%E5%A4%9A%E7%BA%BF%E7%A8%8B/" itemprop="url" rel="index"><span itemprop="name">Java多线程</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="并发编程中的三个主要问题"><a href="#并发编程中的三个主要问题" class="headerlink" title="并发编程中的三个主要问题"></a>并发编程中的三个主要问题</h1><h2 id="可见性问题"><a href="#可见性问题" class="headerlink" title="可见性问题"></a>可见性问题</h2><p>当一个线程修改共享变量的值的时候，其他能否立即得知这个修改，这是第一个问题。</p>
<h2 id="原子性问题"><a href="#原子性问题" class="headerlink" title="原子性问题"></a>原子性问题</h2><p>这里的原子性，有两个尺度。</p>
<ol>
<li>小的尺度上，对于单个变量（主要对long、double而言）的修改是否具有原子性。</li>
<li>大的尺度上，对于一个方法或者代码块的原子性如何保证。</li>
</ol>
<h2 id="有序性问题"><a href="#有序性问题" class="headerlink" title="有序性问题"></a>有序性问题</h2><p>有序性问题在并发编程时是最令人困惑的。</p>
<p>这里先指明，<strong>如果没有作出一些合理的同步工作</strong>，那么<strong>在单个线程中执行顺序具有顺序性</strong>， 但是<strong>在一个线程中观察另一个线程的操作都是不具有有序性的</strong>。</p>
<blockquote>
<p> 正由于上面的问题，才有了这篇文章，其中有的问题这篇文章已经可以回答，有的还不能（主要是对<strong>锁的内存语义</strong>没有阐述）。</p>
</blockquote>
<h1 id="Java-内存模型"><a href="#Java-内存模型" class="headerlink" title="Java 内存模型"></a>Java 内存模型</h1><p>Java线程之间的通信由Java内存模型（也就是JMM）来控制，JMM决定一个线程对共享变量的写入何时对另一个线程可见。</p>
<p>从抽象的角度看，JMM定义了线程和主内存之间的抽象关系：线程之间的共享变量存储在主内存（Main memory）中，每个线程都有一个私有的本地内存（Local memory），本地内存存储了线程读写共享变量的副本。本地内存是JMM的一个抽象，它涵盖cache、store buffer、寄存器以及其他的硬件和编译器优化。</p>
<p><img src="/2021/11/12/Java%E4%B8%AD%E7%9A%84volatile%E5%85%B3%E9%94%AE%E5%AD%97%E3%80%81JMM%E3%80%81happen-before/main-local.png"></p>
<h2 id="重排序和内存屏障"><a href="#重排序和内存屏障" class="headerlink" title="重排序和内存屏障"></a>重排序和内存屏障</h2><h3 id="重排序"><a href="#重排序" class="headerlink" title="重排序"></a>重排序</h3><p>在执行程序的过程中，为了提高性能，编译器和处理器通常会对指令进行重排序。重排序主要分为三类。</p>
<ol>
<li><p>编译器优化的重排序：编译器在不改变单线程语义的情况下，会对执行语句进行重新排序；</p>
</li>
<li><p>指令集重排序：现代操作系统中的处理器都是并行的，如果执行语句之间不存在数据依赖性，处理器可以改变语句的执行顺序；</p>
</li>
<li><p>内存重排序：由于处理器会使用读&#x2F;写缓冲区，出于性能的原因，内存会对读&#x2F;写进行重排序；</p>
</li>
</ol>
<p>上述的 1 属于编译器重排序，2、3属于处理器重排序。这些重排序会导致多线程程序出现内存可见性问题。</p>
<p>对于编译器，JMM的编译器重排序规则会禁止特定类型的编译器重排序；</p>
<p>对于处理器，JMM的处理器重排序规则会要求Java编译器生成指令序列的时插入特定的内存屏障（Memory barrier、Memory fence）。</p>
<h3 id="内存屏障"><a href="#内存屏障" class="headerlink" title="内存屏障"></a>内存屏障</h3><blockquote>
<p>这里我们先来了解一下内存屏障的概念。</p>
</blockquote>
<p>内存屏障也叫做<code>栅栏</code>，它是一种底层原语。它使得 CPU 或编译器在对内存进行操作的时候, 要严格按照一定的顺序来执行, 也就是说在 memory barrier 之前的指令和 memory barrier 之后的指令不会由于系统优化等原因而导致乱序。</p>
<p>内存屏障提供了两个功能。首先，它们通过确保从另一个 CPU 来看屏障的两边的所有指令都是正确的程序顺序；其次它们可以实现内存数据可见性，确保内存数据会同步到 CPU 缓存子系统。</p>
<p>不同计算机体系结构下面的内存屏障也不一样，通常需要认真研读硬件手册来确定，所以我们的主要研究对象是基于 x86 的内存屏障，通常情况下，硬件为我们提供了四种类型的内存屏障。</p>
<ul>
<li><strong>LoadLoad</strong> 屏障</li>
</ul>
<p>它的执行顺序是 Load1 ； LoadLoad ；Load2 ，其中的 Load1 和 Load2 都是加载指令。LoadLoad 指令能够确保执行顺序是在 Load1 之后，Load2 之前，LoadLoad 指令是一个比较有效的防止看到旧数据的指令。</p>
<ul>
<li><strong>StoreStore</strong> 屏障</li>
</ul>
<p>它的执行顺序是 Store1 ；StoreStore ；Store2 ，和上面的 LoadLoad 屏障的执行顺序相似，它也能够确保执行顺序是在 Store1 之后，Store2 之前。</p>
<ul>
<li><strong>LoadStore</strong> 屏障</li>
</ul>
<p>它的执行顺序是 Load1 ； StoreLoad ； Store2 ，保证 Load1 的数据被加载在与这数据相关的 Store2 和之后的 store 指令之前。</p>
<ul>
<li><strong>StoreLoad</strong> 屏障</li>
</ul>
<p>它的执行顺序是 Store1 ； StoreLoad ； Load2 ，确保 Store1 数据对其他处理器可见先于 Load2 及所有后续的访存指令。该屏障会使的此屏障前所有的访存指令完成之后，才会执行此屏障之后的访存指令。</p>
<p>其中<strong>StoreLoad</strong>是一个全能屏障，同时具有其他3个屏障的效果。</p>
<p>此屏障的开销很大，往往需要把写缓冲区（store buffer）中的所有数据全部刷新到主存中（Buffer fully flush）。</p>
<p>也正因此，x86处理器禁止了所有的Load-Load、Store-Store、Load-Store重排序，所以说它具有较强的处理器内存模型。不过对于Store-Load并没有在处理器层面“一棒子打死”，而是把禁止Store-Load重排序的责任交给了更上层，禁止Store-Load重排序的方式就是使用<strong>StoreLoad</strong> 内存屏障。</p>
<h3 id="as-if-serial语义"><a href="#as-if-serial语义" class="headerlink" title="as-if-serial语义"></a>as-if-serial语义</h3><p>正如前面所说，为了提高程序的运行的效率，即使是单线程的程序，每行代码的执行顺序也并非如我们想的那样，完全是顺序的执行的，这好几个步骤的指令重排。</p>
<p>但是，为什么在单线程编程环境下，我们从来没有关心过这些重排序带来的问题。</p>
<p>这就是as-if-serial语义带来的效果，不管编译器、处理器为了提高并行度做出怎样的重排序，单线程程序的结果不能被改变，就好像代码就是真的就是顺序执行的一样。</p>
<p>as-if-serial语义为单线程编程环境下的程序员创造了一个幻觉：单线程程序是按程序的顺序执行的，无心担心内存可见性等问题。</p>
<h3 id="重排序对多线程程序的影响"><a href="#重排序对多线程程序的影响" class="headerlink" title="重排序对多线程程序的影响"></a>重排序对多线程程序的影响</h3><p>举例：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Test</span> &#123;</span><br><span class="line">    <span class="type">boolean</span> <span class="variable">flag</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line">    <span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">write</span><span class="params">()</span> &#123;</span><br><span class="line">        x = <span class="number">1</span>;</span><br><span class="line">        flag = <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">read</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (flag) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> x; <span class="comment">// i 可能等于 0</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在单线程下执行，当然不会有任何异常，原因正如上面的as-if-serial;</p>
<p>但是在多线程下，由于重排序，在read()方法的if代码块中，可能没能读到最新的 x 值。</p>
<p>比如说，A线程先执行 write方法，修改了 flag值，但是此时还没有修改x的值（指令重拍），或者修改了x但是还没有刷回到主存，这个时候B线程执行了read方法，通过率 if的判断条件，但是读 x 的值还是 0 。</p>
<p>这个时候，在多线程程序中，对存在控制依赖的操作重排序，可能改变程序的执行结果。</p>
<p>这是另一个例子：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MemoryReorderingExample</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> <span class="number">0</span>, y = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">int</span> <span class="variable">a</span> <span class="operator">=</span> <span class="number">0</span>, b = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">            i++;</span><br><span class="line">            x = <span class="number">0</span>;</span><br><span class="line">            y = <span class="number">0</span>;</span><br><span class="line">            a = <span class="number">0</span>;</span><br><span class="line">            b = <span class="number">0</span>;</span><br><span class="line">            <span class="type">Thread</span> <span class="variable">t1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">                a = <span class="number">1</span>;</span><br><span class="line">                x = b;</span><br><span class="line">            &#125;);</span><br><span class="line">            <span class="type">Thread</span> <span class="variable">t2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">                b = <span class="number">1</span>;</span><br><span class="line">                y = a;</span><br><span class="line">            &#125;);</span><br><span class="line">            t1.start();</span><br><span class="line">            t2.start();</span><br><span class="line">            t1.join();</span><br><span class="line">            t2.join();</span><br><span class="line">            <span class="type">String</span> <span class="variable">result</span> <span class="operator">=</span> <span class="string">&quot;第&quot;</span> + i + <span class="string">&quot;次(&quot;</span> + x + <span class="string">&quot;,&quot;</span> + y + <span class="string">&quot;)&quot;</span>;</span><br><span class="line">            <span class="keyword">if</span> (x == <span class="number">0</span> &amp;&amp; y == <span class="number">0</span>) &#123;</span><br><span class="line">                System.out.println(result);</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>







<h1 id="volatile的内存语义"><a href="#volatile的内存语义" class="headerlink" title="volatile的内存语义"></a>volatile的内存语义</h1><p>volatile: 为了确保共享变量能被准确和一致地更新，线程应该确保通过排它锁单独的更改这个变量。</p>
<p>Java提供了 <code>volatile</code>，它在多线程中保证了共享变量的可见性——一个线程修改一个共享变量时，另一个线程能读到这个修改值。</p>
<p>它某种程度上是一个轻量级的<code>synchronized</code>。</p>
<p><strong>volatile的特性</strong>：</p>
<ol>
<li>保证共享变量的可见性；</li>
<li>禁止指令重排序的优化；</li>
<li>对单个变量的读写的原子性。</li>
</ol>
<p>下面会对此分别作出解释。</p>
<h2 id="volatile的读-写内存语义"><a href="#volatile的读-写内存语义" class="headerlink" title="volatile的读-写内存语义"></a>volatile的读-写内存语义</h2><ul>
<li><p>volatile写的内存语义：当写一个volatile变量时，JMM会把该线程的本地内存中的共享变量值刷新到主内存。</p>
</li>
<li><p>volatile读的内存语义：当读一个volatile变量时，如果此共享变量值已被另一个线程修改，JMM会把线程对应的本地内存置为无效，然后会从主内存中读取共享变量。</p>
</li>
</ul>
<p>一个关于可见性的测试：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TestVolatile</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">volatile</span> <span class="type">boolean</span> <span class="variable">finished</span> <span class="operator">=</span> <span class="literal">false</span>; <span class="comment">// 去掉 volatile 再看看执行效果</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">doWork</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">long</span> <span class="variable">x</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">while</span> (!finished) &#123;</span><br><span class="line">            ++x;</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(<span class="string">&quot;x is &quot;</span>+x);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">done</span><span class="params">()</span> &#123;</span><br><span class="line">        finished = <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        <span class="type">TestVolatile</span> <span class="variable">test</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TestVolatile</span>();</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(test::doWork).start();</span><br><span class="line">        Thread.sleep(<span class="number">3000</span>);</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(test::done).start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h2 id="volatile的禁止重排序规则"><a href="#volatile的禁止重排序规则" class="headerlink" title="volatile的禁止重排序规则"></a>volatile的禁止重排序规则</h2><p>为了优化代码执行顺序，前面提到过编译器和处理器的重排序。</p>
<p>为了实现volatile的语义，JMM会限制这两种类型的重排序类型。</p>
<p><strong>JMM规则</strong>:</p>
<ol>
<li><p>当第二个操作是volatile写时，不管第一个操作是什么，都不允许重排序；</p>
</li>
<li><p>当第一个操作是volatile读时，不管第二个操作是什么，都不能重排序；</p>
</li>
<li><p>第一个操作是volatile写，第二个操作是volatile读时，不允许重排序。</p>
</li>
</ol>
<p>其中，第1条保证，在volatile写之前的所有的普通写在执行完 volatile写后都会被刷新到主内存，换言之，如果volatile写没有执行，前面的普通写也不会产生影响（也就是说其他线程不可见）。这个性质对于普通是不具备的（下面会举个例子）</p>
<p>其中，第2条保证，在volatile读之前，下面的普通读不会执行，直到volatile读执行完，才能执行下面的普通读。</p>
<p>其中，第3条的保证是自然要有的。</p>
<p>值得注意的是，第1条、第2条实际上让<strong>volatile写-读</strong>在内存读写顺序上具有<strong>锁的释放-获取</strong>语义。</p>
<p><strong>volatile写对应锁的释放，volatile读对应锁的获取。</strong></p>
<p>注意一定只有在一个线程在将锁释放之后，另一个线程可以获取锁。</p>
<p>这让 volatile 具有一定的有序性。</p>
<blockquote>
<p>注意，上述的性质是在JSR-133标准（JDK-5）发布之后才有的语义，之前的volatile的语义比较弱，而这会给程序员带来了很多违反直觉的结果。</p>
</blockquote>
<p>举例如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TestVolatile</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="type">boolean</span> <span class="variable">flag</span> <span class="operator">=</span> <span class="literal">false</span>; <span class="comment">// #1</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">write</span><span class="params">()</span> &#123;</span><br><span class="line">        x = <span class="number">1</span>;</span><br><span class="line">        flag = <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">read</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">y</span> <span class="operator">=</span> -<span class="number">1</span>;</span><br><span class="line">        <span class="keyword">if</span> (flag) &#123;</span><br><span class="line">            y = x; <span class="comment">// #2</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>在多线程环境下，#2处 y 一定为 1 吗？答案是不确定的。这就是违反程序员直觉的结果。</p>
<p>因为程序没有得到正确的同步处理，所以多线程并发下的结果很可能是混乱的。</p>
<p>同步的方法有很多，比如给<code>write()</code>、<code>read()</code>加上<code>synchronized</code>修饰。</p>
<p>不过这里还有更高效的同步方法，就是在#1处用<code>volatile</code>修饰flag变量，根据volatile的指令重排序规则，</p>
<p>一样可以做到访问变量的有序性，此时#1出y的值必然是 1 。</p>
<h2 id="volatile对非原子性约定的修正"><a href="#volatile对非原子性约定的修正" class="headerlink" title="volatile对非原子性约定的修正"></a>volatile对非原子性约定的修正</h2><p>首先讲一个冷知识，在32位机器上，java对64位长度的数据类型（double、long）的读写<strong>可能不是原子性的</strong>。</p>
<p>换言之，<strong>有可能</strong>一个线程在读写double、long变量读的结果既不是自己修改的也不是其它线程修改的，而是一个错误的混乱的值（比如其中前32位来自一个线程，后32位来自另一个线程）。这就带来了一定的风险（虽然这个风险很小很小）。</p>
<p>所以，如果确保一定没有这样的风险，就需要使用volatile修饰double、long变量。</p>
<p>因为，<strong>对于任何单个volatile变量的读写是具有原子性的</strong>。（注意，i++这种经典的<strong>读改写操作</strong>可不是原子的）</p>
<h2 id="volatile内存语义的实现"><a href="#volatile内存语义的实现" class="headerlink" title="volatile内存语义的实现"></a>volatile内存语义的实现</h2><h3 id="共享内存的可见性"><a href="#共享内存的可见性" class="headerlink" title="共享内存的可见性"></a>共享内存的可见性</h3><p>volatile的两条实现原则：</p>
<ol>
<li><p>lock前缀指令会让处理器缓存写回到主内存。</p>
<p>锁住总线：从而独占任何共享内存。</p>
<p>锁住缓存：锁定某块共享内存的缓存并写回到内存，并用缓存一致性协议（MESI）确保修改的原子性，这常常称为”缓存锁定“。</p>
<p>缓存一致性机制会阻止同时修改由两个处理器缓存的内存区域数据。</p>
</li>
<li><p>一个处理器的缓存会写到内存的操作会导致其他处理器的缓存无效。</p>
<p>每个处理器通过嗅探在总线上传播来的数据来检查自己的缓存值是不是已经过期了，当处理器发现自己缓存行对应中有内存地址对应的数据被修改，就会把该缓存行（cache line）设置为无效，需要这个值时重新到主存中获取。</p>
</li>
</ol>
<p>比如<code>lock addl $0x0,(%esp)</code>就是实际上的有lock前缀的汇编命令（这是个空操纵，因为不允许单独使用lock）。</p>
<h3 id="禁止指令优化重排序"><a href="#禁止指令优化重排序" class="headerlink" title="禁止指令优化重排序"></a>禁止指令优化重排序</h3><p>正如上面所言，我们需要使用内存屏障来禁止重排序。</p>
<p>具体的，基于保守策略的JMM按如下规则插入内存屏障：</p>
<ol>
<li>每个volatile写前面插入StoreStore屏障；</li>
<li>每个volatile写后面插入StoreLoad屏障；</li>
<li>每个volatile读后面插入LoadLoad屏障；</li>
<li>每个volatile读后面插入LoadStore屏障；</li>
</ol>
<p>在上面说到，x86机器本身就有着较为安全的内存模型，在处理器层面就禁止了Store-Store、Load-Load、Load-Store 重排序。</p>
<p>所以，在x86机器上，仅仅需要在每个volatile写后面插入StoreLoad屏障即可。</p>
<p>那么这个内存屏障究竟是什么呢？</p>
<p>实际上仍然依赖使用带有Lock前缀的汇编代码来完成。</p>
<h1 id="happens-before"><a href="#happens-before" class="headerlink" title="happens-before"></a>happens-before</h1><h2 id="happen-before的作用"><a href="#happen-before的作用" class="headerlink" title="happen-before的作用"></a>happen-before的作用</h2><p>我们先不讲happen-before的定义和规则，先旗帜鲜明地指出<strong>happen-before的价值</strong>。</p>
<p>正因为上面的一系列的内存屏障等等底层的细节，让Java工作组觉得对于开发者极不友好，很难在跨平台的编程环境中编写出可靠的多线程程序，他们提出了happen-before这个规则， 或者说是工具，帮助程序在不了解内存屏障这些概念的情况下依旧可以对他们的程序的执行顺序有个清醒的判断。</p>
<h2 id="happen-before的定义"><a href="#happen-before的定义" class="headerlink" title="happen-before的定义"></a>happen-before的定义</h2><p>JSR-133中对Java内存模型<strong>happen-before</strong>的定义如下：</p>
<p><code>happen-before</code>是 Java 模型中定义的两项操作的偏序关系。</p>
<ol>
<li>如果一个操作happen-before另一个操作，那么<strong>第一个操作的执行结果将对第二个操作可见</strong>，而且<strong>第一个操作的执行顺序可认为排在第二个操作之前</strong>。</li>
<li>两个操作之间存在happen-before关系，并不意味着Java平台具体实现必须要按照happen-before关系指定的顺序来执行，如果重排序之后的结果，与按happen-before关系来执行的结果一致，那么这种重排序也是合理的，JMM并不禁止。</li>
</ol>
<blockquote>
<p>注意第二个含义，是“可以认为”第一个执行顺序排在第二个操作“，意思是，即使由于重排序导致时间上第二个操作排在第一个操作之前，它的执行效果也可以等同于”第一个执行顺序排在第二个操作“。</p>
</blockquote>
<p>上面的(1)是对程序员的承诺：如果 A happens-before B，那么A的操作结果将对 B 可见，并且执行结果正如A的执行顺序在B之前那样。</p>
<p>上面的(2)是对编译器和处理器排序的约束原则：在不改变程序的语义的情况下，编译器和处理器怎么优化都是可以的。</p>
<h2 id="happen-before的规则"><a href="#happen-before的规则" class="headerlink" title="happen-before的规则"></a>happen-before的规则</h2><ul>
<li>程序次序规则：一个线程内，按照执行流顺序，书写在前面的操作先行发生于书写在后面的操作;</li>
<li>锁定规则：一个 unLock 操作先行发生于后面对同一个锁的 lock 操作，注意这个后面指的是”时间上的先后“；</li>
<li>volatile 变量规则：对一个volatile变量的写操作先行发生于后面对这个volatile变量的读操作，注意这个后面指的是”时间上的先后“;</li>
<li>传递规则：如果操作 A 先行发生于操作 B，而操作 B 又先行发生于操作 C，则可以得出操作 A 先行发生于操作C;</li>
<li>线程启动规则：Thread 对象的 <code>start()</code> 方法先行发生于此线程的每个一个动作;</li>
<li>线程中断规则：对线程<code>interrupt()</code>方法的调用先行发生于被中断线程的代码检测到中断事件的发生;</li>
<li>线程终结规则：线程中所有的操作都先行发生于线程的终止检测，我们可以通过 <code>Thread.join()</code> 方法结束、<code>Thread.isAlive()</code>的返回值手段检测到线程已经终止执行;</li>
<li>对象终结规则：一个对象的初始化完成先行发生于他的<code>finalize()</code>方法的开始。</li>
</ul>
<blockquote>
<p>这 8 条原则摘自《深入理解Java虚拟机》。</p>
</blockquote>
<p>其中第三条规则，尤其需要注意一下。</p>
<p>这 8 条规则中，前 4 条规则是比较重要的，后 4 条规则都是显而易见的。</p>
<p>所以，只有在可以使用上面的8条规则的前提下，才能使用happen-before的规则， 进而判断程序的执行顺序。</p>
<p>否则不能仅靠你的直觉判断，那完全不可信。</p>
<h2 id="happen-before与时间上的先后顺序的关系"><a href="#happen-before与时间上的先后顺序的关系" class="headerlink" title="happen-before与时间上的先后顺序的关系"></a>happen-before与时间上的先后顺序的关系</h2><p>先说答案，二者之间没有必然的关系。</p>
<p>一一解释。</p>
<ol>
<li><p>A happen-before B, A 在时间上就排在B前面吗？</p>
<p>不一定。前面已经说了，在单线程中as-serial就是来确保这个事情的，就是两个无数据依赖关系的变量的读写顺序可以被交换，但是仍然具有happen-before关系。</p>
</li>
<li><p>A 在时间上就排在B前面，A 就 happen-before B 吗?</p>
<p>happen-before的定义要求中有<strong>操作的可见性</strong>，但是在多线程中，如果有正确的同步操作（synchronized、volatile等），那么先发生的操作也不一定对发生的操作可见。</p>
</li>
</ol>
<h2 id="使用happen-before解释DoubleCheckLock"><a href="#使用happen-before解释DoubleCheckLock" class="headerlink" title="使用happen-before解释DoubleCheckLock"></a>使用happen-before解释DoubleCheckLock</h2><p>下面是使用双重检查锁实现单例的经典代码。（为什么这么写，就不细讲了）</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//懒汉式</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Singleton</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">volatile</span> <span class="keyword">static</span> <span class="type">Singleton</span> <span class="variable">singleton</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> Singleton <span class="title function_">getSingleton</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span>(singleton==<span class="literal">null</span>)&#123;</span><br><span class="line">            <span class="keyword">synchronized</span> (Singleton.class)&#123;</span><br><span class="line">                <span class="keyword">if</span>(singleton==<span class="literal">null</span>)&#123;</span><br><span class="line">                    singleton = <span class="keyword">new</span> <span class="title class_">Singleton</span>();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> singleton;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<p>这里我只讲为什么 <code>volatile</code>可以实现代码重排序的效果。</p>
<p>使用的工具正是happen-before。</p>
<p><img src="/2021/11/12/Java%E4%B8%AD%E7%9A%84volatile%E5%85%B3%E9%94%AE%E5%AD%97%E3%80%81JMM%E3%80%81happen-before/dlc.png"></p>
<p>根据happe-before的第一条规则（<strong>程序次序规则</strong>），可以得到各个线程内的蓝色线（代表happen-before的偏序关系）。</p>
<p>根据<strong>volatile 变量规则</strong>，可以得到红色的那条线。</p>
<p>最后根据<strong>传递性规则</strong>，可以得出5个操作的全序关系，这就做到了对象的安全访问。</p>
<p>（多说一句，本质上volatile是通过禁止线程A的2、3的重排序实现happen-before的）。</p>
<p>反过来，如果没有volatile修饰，那么那条红色的线也就不复存在，自然操作5完全可能排在操作2的前面，从而导致对象的不安全访问。</p>
<h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><p><a target="_blank" rel="noopener" href="https://www.zhihu.com/question/296949412">既然CPU有缓存一致性协议（MESI），为什么JMM还需要volatile关键字？</a></p>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/xiaowenmu1/article/details/89705740">MESI（缓存一致性协议）</a></p>
<p><a target="_blank" rel="noopener" href="https://zh.wikipedia.org/zh-hans/MESI%E5%8D%8F%E8%AE%AE">MESI协议-wiki</a></p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/11/12/KMP%E7%AE%97%E6%B3%95%20%E2%80%94%E2%80%94%20%E7%9F%A5%E8%AF%86%E7%82%B9%E3%80%81%E6%A8%A1%E6%9D%BF%E3%80%81%20%E5%BA%94%E7%94%A8/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/11/12/KMP%E7%AE%97%E6%B3%95%20%E2%80%94%E2%80%94%20%E7%9F%A5%E8%AF%86%E7%82%B9%E3%80%81%E6%A8%A1%E6%9D%BF%E3%80%81%20%E5%BA%94%E7%94%A8/" class="post-title-link" itemprop="url">KMP算法 —— 知识点、模板、 应用</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-11-12 01:18:45" itemprop="dateCreated datePublished" datetime="2021-11-12T01:18:45+08:00">2021-11-12</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-01-03 20:25:39" itemprop="dateModified" datetime="2023-01-03T20:25:39+08:00">2023-01-03</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E7%AE%97%E6%B3%95/" itemprop="url" rel="index"><span itemprop="name">算法</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <blockquote>
<p>这里并不打算去详细推导KMP的原理，以及精髓。只是整理了一下KMP算法的模板、应用、理解的关键点。</p>
</blockquote>
<h1 id="基本功能"><a href="#基本功能" class="headerlink" title="基本功能"></a>基本功能</h1><p>KMP算法最基本的应用就是字符串的匹配——给定一个text串，一个pattern串，询问pattern串在text串中是否出现，以及出现几次、位置等等。</p>
<h1 id="模板"><a href="#模板" class="headerlink" title="模板"></a>模板</h1><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">KMP</span>&#123;</span><br><span class="line">    string pattern;</span><br><span class="line">    <span class="type">int</span> len ;</span><br><span class="line">    vector&lt;<span class="type">int</span>&gt; next;  <span class="comment">// 这里的next数组是从1开始的</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// 也叫失配数组,next[i] 表示以第i位结尾的子串与模式串的前缀相同的最大长度（但不能是自身），也就是所谓的最长公共前后缀</span></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">Next</span><span class="params">()</span></span>&#123;</span><br><span class="line">        next.<span class="built_in">resize</span>(len+<span class="number">1</span>);</span><br><span class="line">        <span class="type">int</span> j = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">2</span>; i &lt;= len; i++) &#123;</span><br><span class="line">            <span class="keyword">while</span>(j &gt; <span class="number">0</span> &amp;&amp; pattern[i] != pattern[j + <span class="number">1</span>]) &#123;</span><br><span class="line">                j = next[j];</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span>(pattern[i] == pattern[j + <span class="number">1</span>]) &#123;</span><br><span class="line">                j++;</span><br><span class="line">            &#125;</span><br><span class="line">            next[i] = j;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="built_in">KMP</span>(<span class="type">const</span> string &amp;pattern) &#123;</span><br><span class="line">        len = pattern.<span class="built_in">size</span>();</span><br><span class="line">        <span class="keyword">this</span>-&gt;pattern = <span class="string">&quot; &quot;</span> + pattern; </span><br><span class="line">        <span class="built_in">Next</span>();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="type">bool</span> <span class="title">isSubstringOfText</span><span class="params">(<span class="type">const</span> string&amp; text)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">getPositions</span>(text).<span class="built_in">size</span>() &gt; <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line"></span><br><span class="line">    <span class="comment">// 统计模式串在文本串出现的次数</span></span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">getTimes</span><span class="params">(<span class="type">const</span> string&amp; text)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">getPositions</span>(text).<span class="built_in">size</span>();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 统计模式串在文本串的出现的次数</span></span><br><span class="line">    <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">getPositions</span><span class="params">(<span class="type">const</span> string&amp; text)</span> </span>&#123;</span><br><span class="line">        vector&lt;<span class="type">int</span>&gt; res;</span><br><span class="line">        <span class="keyword">if</span>(text.<span class="built_in">size</span>()  &lt; len) <span class="keyword">return</span> res;</span><br><span class="line">        <span class="type">int</span> j = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; text.<span class="built_in">size</span>(); i++)&#123;</span><br><span class="line">            <span class="keyword">while</span> (j &gt; <span class="number">0</span> &amp;&amp; text[i] != pattern[j + <span class="number">1</span>])&#123;</span><br><span class="line">                j = next[j];</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span>(text[i] == pattern[j + <span class="number">1</span>])&#123;</span><br><span class="line">                j++;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span>(j == len)&#123;</span><br><span class="line">                <span class="comment">// i 是匹配时文本串的位置（从0开始）</span></span><br><span class="line">                res.<span class="built_in">push_back</span>(i);</span><br><span class="line">                j = next[j];</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;;</span><br><span class="line">    </span><br><span class="line"></span><br><span class="line">    <span class="comment">// 求数组f[] 其中 f[i] 表示文本串以i结尾的子串与 pattern 串的前缀相同的最大长度（这里也是从1开始计数）</span></span><br><span class="line">    <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">getFArray</span><span class="params">(<span class="type">const</span> string&amp; text)</span> </span>&#123;</span><br><span class="line">        <span class="type">int</span> m = text.<span class="built_in">size</span>(), j = <span class="number">0</span>;</span><br><span class="line">        <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">f</span><span class="params">(m+<span class="number">1</span>)</span></span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i= <span class="number">0</span> ;i &lt; m; i++)&#123;</span><br><span class="line">            <span class="keyword">while</span>(j &gt; <span class="number">0</span> &amp;&amp; (j == len || text[i] != pattern[j + <span class="number">1</span>])) &#123;</span><br><span class="line">                j = next[j];</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span>(text[i] == pattern[j + <span class="number">1</span>])&#123;</span><br><span class="line">                j++;</span><br><span class="line">            &#125;</span><br><span class="line">            f[i] = j;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> f;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">const</span> vector&lt;<span class="type">int</span>&gt; &amp;<span class="title">getNext</span><span class="params">()</span> <span class="type">const</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> next;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h1 id="知识点"><a href="#知识点" class="headerlink" title="知识点"></a>知识点</h1><ul>
<li>失配数组的求法以及含义。<code>next[]</code></li>
<li>模式匹配的结果数组。<code>f[]</code></li>
<li>从DFA(有限状态自动)的角度去理解。(对后面的AC自动机的理解大有裨益)</li>
</ul>
<h1 id="典型应用"><a href="#典型应用" class="headerlink" title="典型应用"></a>典型应用</h1><ul>
<li>模式串和文本串的模式匹配。</li>
<li>处理和前缀和后缀有关的问题。</li>
<li>处理循环节方面的问题。</li>
</ul>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/11/11/%E5%A4%A7%E6%95%B4%E6%95%B0%E5%8F%96%E6%A8%A1%E3%80%81%E6%9C%80%E5%A4%A7%E5%85%AC%E7%BA%A6%E6%95%B0%E4%B8%8E%E6%9C%80%E5%B0%8F%E5%85%AC%E5%80%8D%E6%95%B0%E3%80%81%E5%BF%AB%E9%80%9F%E5%B9%82%E4%B8%8E%E6%85%A2%E9%80%9F%E4%B9%98/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/11/11/%E5%A4%A7%E6%95%B4%E6%95%B0%E5%8F%96%E6%A8%A1%E3%80%81%E6%9C%80%E5%A4%A7%E5%85%AC%E7%BA%A6%E6%95%B0%E4%B8%8E%E6%9C%80%E5%B0%8F%E5%85%AC%E5%80%8D%E6%95%B0%E3%80%81%E5%BF%AB%E9%80%9F%E5%B9%82%E4%B8%8E%E6%85%A2%E9%80%9F%E4%B9%98/" class="post-title-link" itemprop="url">大整数取模、最大公约数与最小公倍数、快速幂与慢速乘、矩阵乘法加速</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-11-11 00:00:00" itemprop="dateCreated datePublished" datetime="2021-11-11T00:00:00+08:00">2021-11-11</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-01-03 20:24:48" itemprop="dateModified" datetime="2023-01-03T20:24:48+08:00">2023-01-03</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E7%AE%97%E6%B3%95/" itemprop="url" rel="index"><span itemprop="name">算法</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h2 id="大整数取模"><a href="#大整数取模" class="headerlink" title="大整数取模"></a>大整数取模</h2><p>Description：<br>输入一个字符串 a 代表一个很大的数，一个32位整数b，问a能否整除b。<br><a target="_blank" rel="noopener" href="https://vjudge.net/problem/LightOJ-1214">Large Division LightOJ - 1214 
</a><br>亲测 10ms。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;cmath&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;math.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;algorithm&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> ll long long</span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">//大整数取模</span></span><br><span class="line"></span><br><span class="line">string a;</span><br><span class="line"><span class="type">int</span> b;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="type">int</span> t;</span><br><span class="line">    cin&gt;&gt;t;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=t;i++)&#123;</span><br><span class="line">        cin&gt;&gt;a&gt;&gt;b;</span><br><span class="line">        b = <span class="built_in">abs</span>(b);</span><br><span class="line">        ll sum=<span class="number">0</span>;</span><br><span class="line">        <span class="keyword">if</span>(a[<span class="number">0</span>]==<span class="string">&#x27;-&#x27;</span>) a.<span class="built_in">erase</span>(a.<span class="built_in">begin</span>());</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> j=<span class="number">0</span>;j&lt;a.<span class="built_in">length</span>();j++)&#123;</span><br><span class="line">            sum = sum*<span class="number">10</span> + a[j]-<span class="string">&#x27;0&#x27;</span>;</span><br><span class="line">            sum%=b;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span>(sum&gt;<span class="number">0</span>) <span class="built_in">printf</span>(<span class="string">&quot;Case %d: not divisible\n&quot;</span>,i);</span><br><span class="line">        <span class="keyword">else</span> <span class="built_in">printf</span>(<span class="string">&quot;Case %d: divisible\n&quot;</span>,i);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>亲测，500ms左右，发现Java的BigDecimal速度还是比较慢的。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.math.BigDecimal;</span><br><span class="line"><span class="keyword">import</span> java.util.*;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</span> &#123;</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">		<span class="type">Scanner</span> <span class="variable">sc</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Scanner</span>(System.in);</span><br><span class="line">		<span class="type">int</span> t=sc.nextInt();</span><br><span class="line">		<span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=t;i++) &#123;</span><br><span class="line">			<span class="type">String</span> <span class="variable">as</span> <span class="operator">=</span> sc.next();</span><br><span class="line">			<span class="type">String</span> <span class="variable">bs</span> <span class="operator">=</span> sc.next();</span><br><span class="line">			<span class="type">BigDecimal</span> <span class="variable">a</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BigDecimal</span>(as);</span><br><span class="line">			<span class="type">BigDecimal</span> <span class="variable">b</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BigDecimal</span>(bs);</span><br><span class="line">			<span class="keyword">if</span>(a.divideAndRemainder(b)[<span class="number">1</span>].compareTo(<span class="keyword">new</span> <span class="title class_">BigDecimal</span>(<span class="number">0</span>))==<span class="number">0</span>) &#123;</span><br><span class="line">				System.out.printf(<span class="string">&quot;Case %d: divisible\n&quot;</span>,i);</span><br><span class="line">			&#125;<span class="keyword">else</span> &#123;</span><br><span class="line">				System.out.printf(<span class="string">&quot;Case %d: not divisible\n&quot;</span>,i);</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">		sc.close();</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">其中；</span><br><span class="line">a.divideAndRemainder(b)返回一个数组，[<span class="number">0</span>]是整除数，[<span class="number">1</span>]是余数。</span><br><span class="line">A.compareTo（B）-<span class="number">1</span>,<span class="number">0</span>,<span class="number">1</span>分别对应 &lt;,=,&gt;</span><br></pre></td></tr></table></figure>
<h2 id="最大公约数与最小公倍数"><a href="#最大公约数与最小公倍数" class="headerlink" title="最大公约数与最小公倍数"></a>最大公约数与最小公倍数</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">ll <span class="title">gcd</span><span class="params">(ll a,ll b)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> b==<span class="number">0</span>?a: <span class="built_in">gcd</span>(b,a%b);</span><br><span class="line">&#125;</span><br><span class="line"><span class="function">ll <span class="title">lcm</span><span class="params">(ll a,ll b)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> a/<span class="built_in">gcd</span>(a,b)*b;  <span class="comment">// 先除再乘，否则可能中间溢出</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="快速幂与慢速乘"><a href="#快速幂与慢速乘" class="headerlink" title="快速幂与慢速乘"></a>快速幂与慢速乘</h2><p>小栗子<br>快速幂： ${ {2^7&#x3D;2<em>2^6&#x3D;2</em>(2^3)^2&#x3D;2*(2*(2^2))^2} }$</p>
<p>也就是：${ {2^{2^2+2^1+2^0} } }$<br>时间复杂度：O(logn)</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//快速幂</span></span><br><span class="line"><span class="function">ll <span class="title">pow</span><span class="params">(ll a,ll n,ll p)</span></span>&#123;</span><br><span class="line">    ll ans=<span class="number">1</span>;</span><br><span class="line">    <span class="keyword">while</span>(n)&#123;</span><br><span class="line">        <span class="keyword">if</span>(n&amp;<span class="number">1</span>) ans=ans*a%p;  <span class="comment">//如果无需取模，去掉 %p即可</span></span><br><span class="line">        a=a*a%p;  <span class="comment">// 是 平方</span></span><br><span class="line">        n/=<span class="number">2</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> ans;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>慢速乘$ { { 3<em>7&#x3D;3</em>(2^2+2^1+2^0) } } $</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//慢速乘,大数相乘，速度慢，而且容易溢出</span></span><br><span class="line"><span class="function">ll <span class="title">mul</span><span class="params">(ll a,ll b,ll p)</span></span>&#123;  <span class="comment">//将b分解成 和</span></span><br><span class="line">    ll ans=<span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span>(b)&#123;</span><br><span class="line">        <span class="keyword">if</span>(b&amp;<span class="number">1</span>) ans=(ans+a)%p;</span><br><span class="line">        a=a*<span class="number">2</span>%p;  <span class="comment">//是乘 2</span></span><br><span class="line">        b/=<span class="number">2</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> ans;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>常见问法：<br><a target="_blank" rel="noopener" href="https://vjudge.net/problem/LightOJ-1282">Leading and Trailing LightOJ - 1282</a><br>输出一个大数的最右面3位（%1000即可），和最左面3位。<br>如果要输出最左面的3位呢？<br>${ {n^k&#x3D;10^p} }$<br>p应该是个带小数的浮点数m,取对数 ${ {p&#x3D;k*log_{10}(n)} }$,(p的小数部分)*100取整就是答案，因为p的整数部分只控制多少位。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">fmod</span>(a,b) 浮点数的取余数。<span class="built_in">fmod</span>(a,<span class="number">1</span>)即可取出小数。</span><br></pre></td></tr></table></figure>

<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;cmath&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;math.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;algorithm&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> ll long long</span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> ll long long</span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="comment">//快速幂</span></span><br><span class="line"><span class="function">ll <span class="title">pow</span><span class="params">(ll a,ll n,ll p)</span></span>&#123;</span><br><span class="line">    ll ans=<span class="number">1</span>;</span><br><span class="line">    <span class="keyword">while</span>(n)&#123;</span><br><span class="line">        <span class="keyword">if</span>(n&amp;<span class="number">1</span>) ans=ans*a%p;  <span class="comment">//如果无需取模，去掉 %p即可</span></span><br><span class="line">        a=a*a%p;  <span class="comment">// 是 平方</span></span><br><span class="line">        n/=<span class="number">2</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> ans;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="type">int</span> t;</span><br><span class="line">    cin&gt;&gt;t;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=t;i++)&#123;</span><br><span class="line">        ll n,k;</span><br><span class="line">        <span class="built_in">scanf</span>(<span class="string">&quot;%lld%lld&quot;</span>,&amp;n,&amp;k);</span><br><span class="line">        ll ans2 = <span class="built_in">pow</span>(n, k,<span class="number">1000</span>);</span><br><span class="line">        ll ans1 = <span class="built_in">pow</span>(<span class="number">10.0</span>,<span class="number">2.0</span>+<span class="built_in">fmod</span>((<span class="type">double</span>)k*<span class="built_in">log10</span>((<span class="type">double</span>)n), <span class="number">1</span>));</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;Case %d: %lld %03lld\n&quot;</span>,i,ans1,ans2);</span><br><span class="line">        <span class="comment">//注意题目要求最右边的3位数字，如果3，则应该输出003</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h2 id="矩阵加速"><a href="#矩阵加速" class="headerlink" title="矩阵加速"></a>矩阵加速</h2><p>步骤：</p>
<ol>
<li><strong>求出递推矩阵</strong>（也是最重要的一步）；</li>
<li><strong>矩阵乘法</strong>；</li>
<li><strong>矩阵快速幂</strong>；</li>
<li><strong>矩阵乘向量得出结果</strong>。</li>
</ol>
<p>例： <a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/fei-bo-na-qi-shu-lie-lcof/">剑指 Offer 10- I. 斐波那契数列</a></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">mod</span> <span class="operator">=</span> (<span class="type">int</span>)<span class="number">1e9</span> + <span class="number">7</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">fib</span><span class="params">(<span class="type">int</span> n)</span> &#123;</span><br><span class="line">        <span class="type">int</span>[][] A = &#123;&#123;<span class="number">1</span>, <span class="number">1</span>&#125;, &#123;<span class="number">1</span>, <span class="number">0</span>&#125;&#125;;</span><br><span class="line">        <span class="type">int</span>[][] B = pow(A, n);</span><br><span class="line">        <span class="keyword">return</span> B[<span class="number">1</span>][<span class="number">0</span>]; <span class="comment">// 实际上是 矩阵B*列向量[1,0]的结果</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 矩阵乘法，返回新数组</span></span><br><span class="line">    <span class="type">int</span>[][] mul(<span class="type">int</span>[][] A, <span class="type">int</span>[][] B) &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">m</span> <span class="operator">=</span> A.length, n = B[<span class="number">0</span>].length;</span><br><span class="line">        <span class="type">int</span> <span class="variable">len</span> <span class="operator">=</span> A[<span class="number">0</span>].length;</span><br><span class="line">        <span class="type">int</span>[][] ans = <span class="keyword">new</span> <span class="title class_">int</span>[m][n];</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; m; i++) &#123;</span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>; j &lt; n; j++) &#123;</span><br><span class="line">                <span class="type">int</span> <span class="variable">res</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">                <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">k</span> <span class="operator">=</span> <span class="number">0</span>; k &lt; len; k++) &#123;</span><br><span class="line">                    <span class="comment">// 小心溢出</span></span><br><span class="line">                    res = (<span class="type">int</span>)((res + (<span class="type">long</span>)A[i][k] * B[k][j] ) % mod);</span><br><span class="line">                &#125;</span><br><span class="line">                ans[i][j] = res;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 快速幂计算</span></span><br><span class="line">    <span class="type">int</span>[][] pow(<span class="type">int</span>[][] A, <span class="type">int</span> n) &#123;</span><br><span class="line">        <span class="type">int</span>[][] ans = <span class="keyword">new</span> <span class="title class_">int</span>[A.length][A[<span class="number">0</span>].length];</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; ans.length; i++) &#123;</span><br><span class="line">            ans[i][i] = <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">while</span>(n &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span>(n % <span class="number">2</span> == <span class="number">1</span>) &#123;</span><br><span class="line">                ans = mul(ans, A);</span><br><span class="line">            &#125;</span><br><span class="line">            A = mul(A, A);</span><br><span class="line">            n &gt;&gt;= <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>


      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/11/10/%E8%83%8C%E5%8C%85%E9%97%AE%E9%A2%98%E8%AF%A6%E8%A7%A3/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/11/10/%E8%83%8C%E5%8C%85%E9%97%AE%E9%A2%98%E8%AF%A6%E8%A7%A3/" class="post-title-link" itemprop="url">背包问题详解</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-11-10 12:01:40" itemprop="dateCreated datePublished" datetime="2021-11-10T12:01:40+08:00">2021-11-10</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-01-03 20:25:10" itemprop="dateModified" datetime="2023-01-03T20:25:10+08:00">2023-01-03</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E7%AE%97%E6%B3%95/" itemprop="url" rel="index"><span itemprop="name">算法</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="背包问题"><a href="#背包问题" class="headerlink" title="背包问题"></a>背包问题</h1><h2 id="01背包"><a href="#01背包" class="headerlink" title="01背包"></a>01背包</h2><p>Model: 给定n个物品，第i个物品的体积为Vi,价值为Wi.背包容积为m。每件物品最多选择一次，总体积不超过m,要求总价值最大。</p>
<p> <strong>01背包，时间复杂度 O(nm),空间复杂度 O(nm)</strong></p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;stdio.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;cmath&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;math.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;string&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;string.h&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span><span class="string">&lt;algorithm&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> ll long long</span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="type">const</span> <span class="type">int</span> maxn = <span class="number">1e3</span>;</span><br><span class="line"><span class="type">int</span> n,m,w[maxn],v[maxn],f[maxn][maxn];</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line">    cin&gt;&gt;n&gt;&gt;m;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=n;i++) <span class="built_in">scanf</span>(<span class="string">&quot;%d%d&quot;</span>,&amp;w[i],&amp;v[i]);</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=n;i++)&#123;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> j=<span class="number">0</span>;j&lt;=m;j++) f[i][j]=f[i<span class="number">-1</span>][j];</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> j=v[i];j&lt;=m;j++) f[i][j] = <span class="built_in">max</span>(f[i][j],f[i<span class="number">-1</span>][j-v[i]]+w[i]);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 打表观察</span></span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=n;i++)&#123;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> j=<span class="number">0</span>;j&lt;=m;j++)&#123;</span><br><span class="line">            <span class="built_in">printf</span>(<span class="string">&quot;%3d&quot;</span>,f[i][j]);</span><br><span class="line">        &#125;</span><br><span class="line">        cout&lt;&lt;endl;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p><strong>状态表示</strong>：$f[i][j]$表示把 1，2，……i共 <strong>i</strong> 个物品放入容量为 <strong>j</strong> 的背包中所获得的最大价值。<br><strong>DP方程：</strong></p>
<p>$$f[i][j]&#x3D;max\begin{cases}<br>f[i-1][j]，不选第i个物品\<br>f[i-1][j-v[i]]+w[i]，if(j&gt;&#x3D;v[i]),选第i个物品<br>\end{cases}$$</p>
<p>样例<br>n&#x3D;,m&#x3D;15;<br>w[]  2 3 4 5 10<br>v[]   2 3 3 4 7<br><em>动态结果</em><br><img src="https://img-blog.csdnimg.cn/20200208195835563.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ0ODQ2MzI0,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br>最终答案为  f [n] [m]</p>
<p>用<strong>滚动数组</strong>进行优化<br>空间复杂度被优化为 O(m)</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> d[maxn];</span><br><span class="line"><span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=n;i++)&#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> j=m;j&gt;=<span class="number">0</span>;j--)&#123;</span><br><span class="line">        <span class="keyword">if</span>(j&gt;=v[i]) d[j]=<span class="built_in">max</span>(d[j],d[j-v[i]]+w[i]);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><strong>先使用，后更新</strong><br><strong>j</strong> 必须使用倒序循环，才保证先使用 <strong>i-1</strong> 阶段的状态，之后再更新为 <strong>i</strong> 阶段的状态，i.e. 要保证每个物品只加入一次。</p>
<h3 id="小栗子"><a href="#小栗子" class="headerlink" title="小栗子"></a>小栗子</h3><p>—<a target="_blank" rel="noopener" href="https://vjudge.net/problem/OpenJ_Bailian-4004">数字组合方案</a><br>Description：给定整数a1、a2、…an，判断是否可以从中选出若干数，使它们的和恰好为m,求方案总数。</p>
<p>状态表示：f[i][j] 前 <strong>i</strong> 个数组合得到和为 <strong>j</strong> 的方案总数<br>DP方程：$f[i][j]&#x3D;f[i-1][j]+f[i-1][j-a[i]]$<br>分别对应不取a[i]，取a[i]。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> n,m,f[<span class="number">1000</span>][<span class="number">1000</span>],a[<span class="number">1000</span>];</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line">    cin&gt;&gt;n&gt;&gt;m;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=n;i++) &#123;</span><br><span class="line">        <span class="built_in">scanf</span>(<span class="string">&quot;%d&quot;</span>,&amp;a[i]);</span><br><span class="line">        f[i][<span class="number">0</span>]=<span class="number">1</span>; <span class="comment">// 边界值</span></span><br><span class="line">    &#125;</span><br><span class="line">    f[<span class="number">0</span>][<span class="number">0</span>]=<span class="number">1</span>;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=n;i++)&#123;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> j=<span class="number">0</span>;j&lt;a[i];j++) f[i][j]= f[i<span class="number">-1</span>][j];</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> j=a[i];j&lt;=m;j++) f[i][j]= f[i<span class="number">-1</span>][j]+f[i<span class="number">-1</span>][j-a[i]];</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 打表观察</span></span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;=n;i++)&#123;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> j=<span class="number">0</span>;j&lt;=m;j++)&#123;</span><br><span class="line">            cout&lt;&lt;f[i][j]&lt;&lt;<span class="string">&quot; &quot;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        cout&lt;&lt;endl;</span><br><span class="line">    &#125;</span><br><span class="line">    cout&lt;&lt;f[n][m]&lt;&lt;endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p><img src="https://img-blog.csdnimg.cn/20200208205516607.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQ0ODQ2MzI0,size_16,color_FFFFFF,t_70" alt="在这里插入图片描述"><br><strong>滚动数组优化</strong></p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> n,m,f[<span class="number">10005</span>],a;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line">    cin&gt;&gt;n&gt;&gt;m;</span><br><span class="line">    <span class="comment">//边界值，即前i个数组合，和为0的方案数，方案数为1，也就是，前i个数都不取</span></span><br><span class="line">    f[<span class="number">0</span>]=<span class="number">1</span>;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=n;i++)&#123;</span><br><span class="line">        <span class="built_in">scanf</span>(<span class="string">&quot;%d&quot;</span>,&amp;a);  <span class="comment">//可以边读入边运算</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> j=m;j&gt;=a;j--)&#123;</span><br><span class="line">            f[j]+=f[j-a];</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    cout&lt;&lt;f[m]&lt;&lt;endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h2 id="完全背包"><a href="#完全背包" class="headerlink" title="完全背包"></a>完全背包</h2><p>Model :  给定n个物品，第i个物品的体积为Vi,价值为Wi.背包容积为m。<strong>每件物品可以选择无数次</strong>，总体积不超过m,要求总价值最大。</p>
<p><strong>状态表示</strong>：<code>f[i][j]</code>表示把 1，2，……i共 <strong>i</strong> 个物品放入容量为 <strong>j</strong> 的背包中所获得的最大价值。<br><strong>DP方程：</strong></p>
<p>$$f[i][j]&#x3D;max\begin{cases}<br>f[i-1][j],从未选过第i种物品\<br>f[i][j-v[i]]+w[i]，if (j&gt;&#x3D;v[i]) 从第i种物品中选择一个<br>\end{cases}$$</p>
<p>在 01背包中，使用倒序循环遍历 j ,限制了每种物品最多使用一次，<br>自然，正序循环的话，每个物品就可以重复加入。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=n;i++)&#123;</span><br><span class="line">    <span class="built_in">scanf</span>(<span class="string">&quot;%d%d&quot;</span>,&amp;v,&amp;w);</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> j=v;j&lt;=m;j++)&#123;</span><br><span class="line">        f[j] = <span class="built_in">max</span>(f[j],f[j-v]+w);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="小栗子–自然数拆分"><a href="#小栗子–自然数拆分" class="headerlink" title="小栗子–自然数拆分"></a>小栗子–自然数拆分</h3><p>Description： 给定一个自然数N,要求把N拆分成若干个整数相加的形式，参与加法运算的数可以重复，求拆分方案。<br>思路： 1~N这N个数就是n个物品，总和 N 就是背包的容积.</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> ll mod = <span class="number">2147483648</span>;</span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line">ll f[<span class="number">4005</span>],n;</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line">    cin&gt;&gt;n;</span><br><span class="line">    f[<span class="number">0</span>]=<span class="number">1</span>; <span class="comment">//边界值</span></span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=n;i++)&#123; <span class="comment">//依次加入n个物品</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> j=i;j&lt;=n;j++)&#123; <span class="comment">//已有1~i这i个数求和得到j的方案数</span></span><br><span class="line">            f[j] = (f[j]+f[j-i])%mod;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    cout&lt;&lt;(f[n]%mod)&lt;&lt;endl;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125; </span><br></pre></td></tr></table></figure>

<h2 id="多重背包"><a href="#多重背包" class="headerlink" title="多重背包"></a>多重背包</h2><p> Model:给定n个物品，第i个物品的体积为Vi,价值为Wi.<strong>并且有Ci个</strong>,背包容积为m。总体积不超过m,要求总价值最大。</p>
<h3 id="直接拆分法"><a href="#直接拆分法" class="headerlink" title="直接拆分法"></a>直接拆分法</h3><p>把第 i 种商品看做独立的Ci个商品，相当于一共有  ${ { \sum_{ i &#x3D; 1 }^N Ci } }$个物品，就把物品转化为01背包问题了,但是效率比较低。<br>时间复杂度： $$O(M*\sum_{ i &#x3D; 1 }^N Ci)$$</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span>  f[maxn],n,m,v[maxn],w[maxn],c[maxn];</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=n;i++)&#123;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> j=<span class="number">1</span>;j&lt;=c[i];j++)&#123;</span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> k=m;k&gt;=v[i];k--)&#123;</span><br><span class="line">                f[k] = <span class="built_in">max</span>(f[k],f[k-v[i]]+w[i]);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">printf</span>(<span class="string">&quot;%d\n&quot;</span>,f[m]);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="二进制拆分法"><a href="#二进制拆分法" class="headerlink" title="二进制拆分法"></a>二进制拆分法</h3><p>把数量为Ci个的第 i 种物品拆分成 p+2种物品，它们的价值与体积分别为：<br>$$ 2^0<em>Wi,2^1</em>Wi,……2^p<em>Wi，Ri</em>Wi $$<br>$$2^0<em>Vi,2^1</em>Vi,……2^p<em>Vi，Ri</em>Vi$$<br>其中 p为满足 ${ {2^{p+1}-1&lt;&#x3D;Ci } }$ 的最大整数。<br>${ {Ri&#x3D;Ci-(2^{p+1}-1) } }$。</p>
<p>时间复杂度： $$O(M*\sum_{i&#x3D;1}^N log(Ci))$$</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">int</span> maxn = <span class="number">1e5</span>+<span class="number">5</span>;</span><br><span class="line"><span class="type">int</span> f[maxn],n,m,w[maxn],v[maxn],c[maxn];</span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="keyword">while</span> (<span class="built_in">scanf</span>(<span class="string">&quot;%d%d&quot;</span>,&amp;m,&amp;n)==<span class="number">2</span>) &#123;</span><br><span class="line">        <span class="built_in">memset</span>(f, <span class="number">0</span>, <span class="built_in">sizeof</span>(f));</span><br><span class="line">        <span class="built_in">memset</span>(w, <span class="number">0</span>, <span class="built_in">sizeof</span>(w));</span><br><span class="line">        <span class="built_in">memset</span>(c, <span class="number">0</span>, <span class="built_in">sizeof</span>(c));</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=n;i++)&#123;</span><br><span class="line">            <span class="built_in">scanf</span>(<span class="string">&quot;%d%d&quot;</span>,&amp;c[i],&amp;w[i]);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=n;i++)&#123;</span><br><span class="line">            <span class="type">int</span> p = (<span class="type">int</span>)<span class="built_in">log2</span>(c[i]+<span class="number">1</span>)<span class="number">-1</span>;</span><br><span class="line">            <span class="type">int</span> ri= -<span class="built_in">pow</span>(<span class="number">2</span>, p+<span class="number">1</span>)+c[i]+<span class="number">1</span>;</span><br><span class="line">            <span class="type">int</span> wp[p+<span class="number">5</span>],vp[p+<span class="number">5</span>];</span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> x=<span class="number">0</span>;x&lt;=p;x++)&#123;</span><br><span class="line">                wp[x]=<span class="built_in">pow</span>(<span class="number">2</span>,x)*w[i];</span><br><span class="line">                vp[x]=<span class="built_in">pow</span>(<span class="number">2</span>,x)*v[i]</span><br><span class="line">            &#125;</span><br><span class="line">            wp[p+<span class="number">1</span>]=ri*w[i];v[p+<span class="number">1</span>]=ri*v[i];</span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> j=<span class="number">0</span>;j&lt;=p+<span class="number">1</span>;j++)&#123;</span><br><span class="line">                <span class="keyword">for</span>(<span class="type">int</span> k=m;k&gt;=vp[j];k--)&#123;</span><br><span class="line">                    f[k] = <span class="built_in">max</span>(f[k],f[k-vp[j]]+wp[j]);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">printf</span>(<span class="string">&quot;%d\n&quot;</span>,f[m]);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<h3 id="多重背包模板题"><a href="#多重背包模板题" class="headerlink" title="多重背包模板题."></a><a target="_blank" rel="noopener" href="https://vjudge.net/problem/UVA-10626">多重背包模板题</a>.</h3><h2 id="分组背包"><a href="#分组背包" class="headerlink" title="分组背包"></a>分组背包</h2><p> Model : 给定 <strong>N</strong> 组物品，其中第 <strong>i</strong> 组有 <strong>Ci</strong> 个物品。第 i 组的第 <strong>j</strong> 个物品的体积为 <strong>Vij</strong>,价值为 <strong>Wij</strong>。有一个容积为 M 的背包，使得<strong>每组至多选择一个物品</strong>并且总体积不超过M的前提下，物品的价值和最大。<br><strong>状态</strong>:$F[i][j]$,表示从前 <strong>i</strong> 组中选出总体积为 <strong>j</strong> 的物品放入背包，物品的最大价值和。<br><strong>DP方程</strong>：<br>$$f[i][j]&#x3D;max\begin{cases}<br>f[i-1][j]，不选第 i 组物品\<br>max_{(1&#x3D;&lt;k&lt;&#x3D;C_i)}f[i-1][j-V_{ik}]+W_{ik}，选第i组物品的某个物品k<br>\end{cases}$$</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">1</span>;i&lt;=n;i++)&#123;</span><br><span class="line">    <span class="keyword">for</span>(<span class="type">int</span> j=m;j&gt;=<span class="number">0</span>;j--)&#123;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> k=<span class="number">1</span>;k&lt;=c[i];k++)&#123;</span><br><span class="line">            <span class="keyword">if</span>(j&gt;=v[i][k])</span><br><span class="line">                f[j]=<span class="built_in">max</span>(f[j],f[j-v[i][k]]+w[i][k]);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">printf</span>(<span class="string">&quot;%d\n&quot;</span>,f[m]);</span><br></pre></td></tr></table></figure>
<p>注意点：<strong>倒序循环j</strong>;<br><strong>对每一组物品的循环k应该放在j的内层，以保证每个物品只会放进去一次。</strong><br>从动态规划的角度：<br><strong>i</strong>是”阶段“，<strong>i</strong>和<strong>j</strong>共同构成”状态“，而<strong>k</strong>是”决策“——在第<strong>i</strong>个阶段使用哪个物品。</p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/11/03/MySQL%E7%B3%BB%E5%88%97%E2%80%94%E2%80%94-%E9%94%81%E6%9C%BA%E5%88%B6/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/11/03/MySQL%E7%B3%BB%E5%88%97%E2%80%94%E2%80%94-%E9%94%81%E6%9C%BA%E5%88%B6/" class="post-title-link" itemprop="url">MySQL系列 —— 锁机制</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-11-03 16:11:15" itemprop="dateCreated datePublished" datetime="2021-11-03T16:11:15+08:00">2021-11-03</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2021-11-27 12:23:01" itemprop="dateModified" datetime="2021-11-27T12:23:01+08:00">2021-11-27</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/MySQL/" itemprop="url" rel="index"><span itemprop="name">MySQL</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h3 id="两阶段锁协议"><a href="#两阶段锁协议" class="headerlink" title="两阶段锁协议"></a>两阶段锁协议</h3><p>两阶段锁协议（2PL）是一种能够保证事务可串行化的协议，它将事务的获取锁和释放锁划分成了增长（Growing）和缩减（Shrinking）两个不同的阶段。</p>
<p>在增长阶段，一个事务可以获得锁但是不能释放锁；而在缩减阶段事务只可以释放锁，并不能获得新的锁，如果只看 2PL 的定义，那么到这里就已经介绍完了，但是它还有两个变种：</p>
<ol>
<li><strong>严格两阶段锁</strong>（Strict 2PL）：事务持有的<strong>互斥</strong>锁必须在提交后再释放；</li>
<li><strong>强两阶段锁</strong>（Rigorous 2PL）：事务持有的<strong>所有</strong>锁必须在提交后释放；</li>
</ol>
<h1 id="MySQL中锁机制"><a href="#MySQL中锁机制" class="headerlink" title="MySQL中锁机制"></a>MySQL中锁机制</h1><h2 id="按粒度分"><a href="#按粒度分" class="headerlink" title="按粒度分"></a>按粒度分</h2><p>表锁<br>行锁</p>
<p>简单的说，表锁上锁的粒度是table，而行锁可以只对部分行上锁。</p>
<p>后者粒度低得多，这也是为什么InnoDB引擎是默认的引擎，除非你的数据库操作只有读，没有写和加锁读。</p>
<p>否则表锁的代价太大。</p>
<p><img src="/2021/11/03/MySQL%E7%B3%BB%E5%88%97%E2%80%94%E2%80%94-%E9%94%81%E6%9C%BA%E5%88%B6/granularity-hierarchy.png" alt="granularity-hierarchy"></p>
<h2 id="按是否意向锁区分"><a href="#按是否意向锁区分" class="headerlink" title="按是否意向锁区分"></a>按是否意向锁区分</h2><p>意向锁、非意向锁</p>
<p>（见上图）</p>
<p>虽然这种不同粒度的锁能够解决父节点被加锁时，子节点不能被加锁的问题，但是我们没有办法在子节点被加锁时，立刻确定父节点能不能被加锁。</p>
<p>在这时我们就需要引入<strong>意向锁</strong>来解决这个问题了。</p>
<p><strong>当需要给子节点加锁时，先给所有的父节点加对应的意向锁</strong>，<strong>意向锁之间是完全不会互斥的</strong>，只是用来帮助父节点快速判断是否可以对该节点进行加锁。</p>
<p>具体的，InnoDB如果需要对每一行加上行锁，那么需要先给它所在表加上意向锁。</p>
<p>加的是互斥行锁，就给表加上意向互斥锁（IX）；</p>
<p>加的是共享行锁，就给锁加上意向共享锁（IS）。</p>
<p>见下图，各种锁的兼容性矩阵。</p>
<p><img src="/2021/11/03/MySQL%E7%B3%BB%E5%88%97%E2%80%94%E2%80%94-%E9%94%81%E6%9C%BA%E5%88%B6/lock-type-compatibility-matrix.png" alt="lock-type-compatibility-matrix"></p>
<h2 id="按读写性质分"><a href="#按读写性质分" class="headerlink" title="按读写性质分"></a>按读写性质分</h2><p>读锁（共享锁、S锁）<br>写锁（互斥锁、X锁）</p>
<p>这个锁的语义和其他地方（如Java中的<code>ReadWriteLock</code>）锁的意义是一致的。</p>
<p>读锁-读锁共享；读锁-写锁互斥；写锁-写锁互斥。</p>
<p>写操作加的都是写锁，普通读操作是不加锁的（这个读称之为MVCC的一致性非锁定读）。</p>
<p>如果需要读操作也要加锁，可以使用<code>for share</code>、<code>for update</code>）(前者也就是<code>lock in share mode </code>)。（后面会详细介绍）</p>
<blockquote>
<p> 注意MySQL支持多种存储引擎，不同引擎对锁的支持是不一样的。<br>比如，MyISAM、Memory引擎就不支持行锁，自然也无需表级别的意向锁。<br>重点是InnoDB中的锁机制。</p>
</blockquote>
<h1 id="InnoDB存储引擎中的锁"><a href="#InnoDB存储引擎中的锁" class="headerlink" title="InnoDB存储引擎中的锁"></a>InnoDB存储引擎中的锁</h1><h2 id="InnoDB中的表级锁"><a href="#InnoDB中的表级锁" class="headerlink" title="InnoDB中的表级锁"></a>InnoDB中的表级锁</h2><ul>
<li><strong>表级别的S锁、X锁</strong></li>
</ul>
<ol>
<li><p>默认情况在对一个表执行 select、insert、delete、update语句时, InnoDB引擎不会为这个表加上表级别的锁。</p>
</li>
<li><p>在执行一些DDL语句时，会和select、insert、delete、update产生冲突，一个阻塞另一个。</p>
<p>不过，这个不是表锁起的作用，而是位于服务器层的 <strong>元数据锁</strong>（Metadata Lock）来实现的。</p>
<p>（DDL语句执行时，<strong>会隐式提交当前会话中的事务</strong>，这个要注意)</p>
</li>
<li><p>InnoDB存储引擎提供的表锁其实在一般使用场景下，并不会直接使用到，只有在一些特殊场景下（服务崩溃需要对系统运维时使用）。</p>
</li>
</ol>
<p><strong>如何加表锁</strong></p>
<p>在系统变量为 <code>autocommit = 0</code>和<code>innodb_table_locks = 1</code>时，</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">LOCK TABLES t READ; # 表级别读锁</span><br><span class="line">LOCK TABLES T WRITE; # 表级别的写锁</span><br></pre></td></tr></table></figure>



<ul>
<li><p><strong>表级别的IS、IX锁</strong></p>
<p>在需要加行锁的时候，需要先给所在的表加上意向锁。加意向锁的作用和意图在上面已经说过了。</p>
</li>
<li><p><strong>表级别的AUTO-INC锁</strong></p>
</li>
</ul>
<p>如何实现对具有<strong>AUTO_INCREMENT</strong>属性的列的自增？</p>
<p>这个时候需要使用到表级别的<strong>自增锁</strong>。</p>
<p>注意这个AUTO-INC锁的作用范围只有单个插入语句，在插入语句完成后，这把锁就会被释放。</p>
<p>还有一个更轻量的实现，就是在获取到需要的自增值就立即释放锁，不必等待插入语句完成。</p>
<p>可以使用<code>innodb_autoinc_lock_mode</code>控制使用哪个实现。</p>
<h2 id="InnoDB中的行级锁"><a href="#InnoDB中的行级锁" class="headerlink" title="InnoDB中的行级锁"></a>InnoDB中的行级锁</h2><p>InnoDB中的行级锁有下面几种概念， 共同实现了行锁这个语义。</p>
<ul>
<li><p><strong>Record Lock</strong></p>
<p>记录锁，官方叫它“LOCK_REC_NOT_GAP”，顾名思义，锁住的是真实的数据行。</p>
</li>
<li><p><strong>Gap Lock</strong></p>
<p>间隙锁，”LOCK_GAP”</p>
<p>这个间隙锁，锁住的是数据记录之间的间隙。</p>
<p>为什么要锁住间隙？这个gap lock的提出是为了解决”幻行“现象的发生。</p>
<p>所谓幻读，就是一个事务向另一个事务读范围中插入了一条记录，然后另一个事务就读到了“幻行”（虽然说MVCC的一致性读很大程度上可以避免，但是彻底避免幻读就需要使用gap lock）。</p>
</li>
</ul>
<img src="gap-lock.png" style="zoom:50%;" />

<p>如上图，再给7上间隙锁的时候，其他事务无法向(5,7)之间插入行记录。这就是<strong>锁住间隙</strong>的作用。</p>
<ul>
<li><strong>Next-key Lock</strong></li>
</ul>
<p>临键锁，官方叫它”LOCK_ORDINARY“,从这个英文名字就可以看出。</p>
<p>这个锁才是正常使用的加的行锁。</p>
<img src="next-key-lock.png" style="zoom:50%;" />

<p>如上，临键锁实际上记录锁和间隙锁的合体。给某个记录加上临键锁，实际上锁住了这条记录和它前面的间隙。</p>
<p>所以说临键锁是”左开右闭“的。</p>
<h1 id="InnoDB锁的内存结构"><a href="#InnoDB锁的内存结构" class="headerlink" title="InnoDB锁的内存结构"></a>InnoDB锁的内存结构</h1><p>上面讲到了表锁和行锁。</p>
<p>表锁的对象的是一个表，可以为这张表生成一个锁结构。</p>
<p>如果是行锁呢？难不成为每一个数据行都要生成一个锁结构吧，这个对内存消耗太大了，也不利于管理。</p>
<p>所以，符合下面这些条件的记录的锁实际上使用一个锁结构就可以做到。</p>
<ul>
<li>同一个事务中</li>
<li>加锁的类型一样</li>
<li>等待状态一样</li>
<li>被加锁的记录在同一个页面中</li>
</ul>
<img src="lock-structure.png" style="zoom:30%;" />



<ul>
<li><p><strong>锁所在的事务信息</strong>：无论表级锁还是行级锁，都是属于一个事务的，这里记载着属于这个事务的信息。</p>
</li>
<li><p><strong>索引信息</strong>：对于行级锁而言，需要记录一下加锁的记录属于哪个索引。</p>
</li>
<li><p>表锁或者行锁信息：</p>
<p>如果是行锁的话：</p>
<ul>
<li>Space ID：记录所在表空间。</li>
<li>Page Number：记录所在的页号</li>
<li>n_bits：对弈行级锁而言，一条记录对应一个比特，实际上就是用一个位图结构去记录那些行记录被加了锁，n_bits则是表示使用了多少比特。</li>
</ul>
</li>
<li><p><strong>type mode</strong></p>
<ul>
<li><p><strong>lock_mode</strong>（锁模式）</p>
<ul>
<li>LOCK_IS（共享意向锁）</li>
<li>LOCK_IX（独占意向锁）</li>
<li>LOCK_S（共享锁）</li>
<li>LOCK_X（独占锁）</li>
<li>LOCK_AUTO_INC（AUTO_INC锁）</li>
</ul>
</li>
<li><p><strong>lock_type</strong>（锁类型）</p>
<ul>
<li>LOCK_TABLE（表级锁）</li>
<li>LOCK_REC （行级锁）</li>
</ul>
</li>
<li><p><strong>rec_lock_type</strong> （行锁的具体类型，只有当lock_type为LOCK_REC时才是有效的）</p>
<ul>
<li>LOCK_ORDINARY（next_key临键锁）</li>
<li>LOCK_GAP（间隙锁）</li>
<li>LOCK_REC_NOT_GAP（记录锁）</li>
<li>LOCK_INSERT_INTENTION（插入意向锁）</li>
</ul>
</li>
<li><p><strong>LOCK_WAITING</strong> </p>
<p>1 表示当前事务尚未得到锁，正处于等待状态，0 表示当前事务已经或得到锁。</p>
</li>
</ul>
</li>
</ul>
<p>其实仅仅看这个一个type mode 的结构皆可以知道MySQL锁的整体结构了。</p>
<p><img src="/2021/11/03/MySQL%E7%B3%BB%E5%88%97%E2%80%94%E2%80%94-%E9%94%81%E6%9C%BA%E5%88%B6/typemode.png"></p>
<ul>
<li>其他信息</li>
</ul>
<p>为了更好地管理系统运行过程中生成的锁结构，有哈希表、链表等等数据结构方便管理</p>
<ul>
<li><p>比特数组</p>
<p>n_bits属性指出这个位图有多大，那么这一堆比特位就是用来隐射这一页中有哪些记录加上了锁的。</p>
</li>
</ul>
<h1 id="查看事务的加锁情况"><a href="#查看事务的加锁情况" class="headerlink" title="查看事务的加锁情况"></a>查看事务的加锁情况</h1><p>也许需要打开一些配置项：</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SET</span> <span class="keyword">GLOBAL</span> innodb_status_output<span class="operator">=</span><span class="keyword">ON</span>;</span><br><span class="line"><span class="keyword">SET</span> <span class="keyword">GLOBAL</span> innodb_status_output_locks<span class="operator">=</span><span class="keyword">ON</span>;</span><br></pre></td></tr></table></figure>

<p>在MySQL Clinet中使用<code>show engine innodb status</code></p>
<p>这里只列出关于事务和锁的信息，其余信息省略：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta prompt_"># </span><span class="language-bash">当前活动的事务</span> </span><br><span class="line">TRANSACTIONS</span><br><span class="line">------------</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">下一个分配的事务ID信息</span></span><br><span class="line">Trx id counter 7203081</span><br><span class="line">Purge done for trx&#x27;s n:o &lt; 7203074 undo n:o &lt; 0 state: running but idle</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">每个回滚段中都有一个History链表，链表的长度为 57</span></span><br><span class="line">History list length 57</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">下面对于每一个会话列出它的事务信息</span></span><br><span class="line">LIST OF TRANSACTIONS FOR EACH SESSION:</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash"> 每一个<span class="string">&#x27;---TRANSACTION&#x27;</span> 后面跟着响应的事务的信息</span></span><br><span class="line">---TRANSACTION 422190990756560, not started</span><br><span class="line">0 lock struct(s), heap size 1136, 0 row lock(s)</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">事务ID7203080, 活跃了 163 秒</span></span><br><span class="line">---TRANSACTION 7203080, ACTIVE 163 sec</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">2个锁结构， 7个行锁（实际上只有6条记录）（这里的锁结构指的就是前面的锁的内存结构）</span></span><br><span class="line">2 lock struct(s), heap size 1136, 7 row lock(s)</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">MySQL内部线程号、操作系统中的线程号等信息</span></span><br><span class="line">MySQL thread id 17, OS thread handle 13048123392, query id 389 localhost 127.0.0.1 root</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">锁结构1. 表级别的意向互斥锁</span></span><br><span class="line">TABLE LOCK table `test_lock`.`test_lock` trx id 7203080 lock mode IX</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">锁结构2. 记录锁（</span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">详细解释：表空间号是106，页号是4，锁的位图大小为80，索引是PRIMARY聚簇索引，X型实际上是next-key-lock，（aka 临键锁）</span></span><br><span class="line">RECORD LOCKS space id 106 page no 4 n bits 80 index PRIMARY of table `test_lock`.`test_lock` trx id 7203080 lock_mode X</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">紧接着就是加锁记录的详细信息</span></span><br><span class="line">Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0</span><br><span class="line"> 0: len 8; hex 73757072656d756d; asc supremum;;</span><br><span class="line"></span><br><span class="line">Record lock, heap no 2 PHYSICAL RECORD: n_fields 6; compact format; info bits 0</span><br><span class="line"> 0: len 4; hex 80000000; asc     ;;</span><br><span class="line"> 1: len 6; hex 0000002e0502; asc    .  ;;</span><br><span class="line"> 2: len 7; hex 0100000164102a; asc     d *;;</span><br><span class="line"> 3: len 4; hex 80000000; asc     ;;</span><br><span class="line"> 4: len 4; hex 80000000; asc     ;;</span><br><span class="line"> 5: len 4; hex 80000000; asc     ;;</span><br><span class="line"></span><br><span class="line">Record lock, heap no 3 PHYSICAL RECORD: n_fields 6; compact format; info bits 0</span><br><span class="line"> 0: len 4; hex 8000000a; asc     ;;</span><br><span class="line"> 1: len 6; hex 0000002e0502; asc    .  ;;</span><br><span class="line"> 2: len 7; hex 01000001641060; asc     d `;;</span><br><span class="line"> 3: len 4; hex 8000000a; asc     ;;</span><br><span class="line"> 4: len 4; hex 8000000a; asc     ;;</span><br><span class="line"> 5: len 4; hex 8000000a; asc     ;;</span><br><span class="line"></span><br><span class="line">Record lock, heap no 4 PHYSICAL RECORD: n_fields 6; compact format; info bits 0</span><br><span class="line"> 0: len 4; hex 80000014; asc     ;;</span><br><span class="line"> 1: len 6; hex 0000002e0551; asc    . Q;;</span><br><span class="line"> 2: len 7; hex 010000012c112d; asc     , -;;</span><br><span class="line"> 3: len 4; hex 80000014; asc     ;;</span><br><span class="line"> 4: len 4; hex 80000014; asc     ;;</span><br><span class="line"> 5: len 4; hex 80000014; asc     ;;</span><br><span class="line"></span><br><span class="line">Record lock, heap no 5 PHYSICAL RECORD: n_fields 6; compact format; info bits 0</span><br><span class="line"> 0: len 4; hex 8000001e; asc     ;;</span><br><span class="line"> 1: len 6; hex 0000002e0502; asc    .  ;;</span><br><span class="line"> 2: len 7; hex 01000001641100; asc     d  ;;</span><br><span class="line"> 3: len 4; hex 8000001e; asc     ;;</span><br><span class="line"> 4: len 4; hex 8000001e; asc     ;;</span><br><span class="line"> 5: len 4; hex 8000001e; asc     ;;</span><br><span class="line"></span><br><span class="line">Record lock, heap no 6 PHYSICAL RECORD: n_fields 6; compact format; info bits 0</span><br><span class="line"> 0: len 4; hex 80000028; asc    (;;</span><br><span class="line"> 1: len 6; hex 0000002e13cd; asc    .  ;;</span><br><span class="line"> 2: len 7; hex 02000001591605; asc     Y  ;;</span><br><span class="line"> 3: len 4; hex 80000028; asc    (;;</span><br><span class="line"> 4: len 4; hex 80000028; asc    (;;</span><br><span class="line"> 5: len 4; hex 80000028; asc    (;;</span><br><span class="line"></span><br><span class="line">Record lock, heap no 7 PHYSICAL RECORD: n_fields 6; compact format; info bits 0</span><br><span class="line"> 0: len 4; hex 80000032; asc    2;;</span><br><span class="line"> 1: len 6; hex 0000002e0990; asc    .  ;;</span><br><span class="line"> 2: len 7; hex 020000013e14d7; asc     &gt;  ;;</span><br><span class="line"> 3: len 4; hex 80000032; asc    2;;</span><br><span class="line"> 4: len 4; hex 80000032; asc    2;;</span><br><span class="line"> 5: len 4; hex 80000032; asc    2;;</span><br><span class="line"></span><br><span class="line">--------</span><br></pre></td></tr></table></figure>



<h2 id="死锁"><a href="#死锁" class="headerlink" title="死锁"></a>死锁</h2><p>死锁示例sql：</p>
<p>事务1</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">start</span> transaction ;</span><br><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> test_lock <span class="keyword">where</span> primary_id <span class="operator">=</span> <span class="number">10</span> <span class="keyword">for</span> <span class="keyword">update</span> ;</span><br><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> test_lock <span class="keyword">where</span> primary_id <span class="operator">=</span> <span class="number">20</span> <span class="keyword">for</span> <span class="keyword">update</span> ;</span><br></pre></td></tr></table></figure>

<p>事务2</p>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">start</span> transaction ;</span><br><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> test_lock <span class="keyword">where</span> primary_id <span class="operator">=</span> <span class="number">20</span> <span class="keyword">for</span> <span class="keyword">update</span> ;</span><br><span class="line"><span class="keyword">select</span> <span class="operator">*</span> <span class="keyword">from</span> test_lock <span class="keyword">where</span> primary_id <span class="operator">=</span> <span class="number">10</span> <span class="keyword">for</span> <span class="keyword">update</span> ;</span><br></pre></td></tr></table></figure>



<p><strong>InnoDB的死锁检测机制</strong></p>
<p>当它检测到死锁发生时，会选择一个较小的事务（所谓较小的事务，就是在事务执行过程中插入、更新后删除的记录较少的事务）进行回滚（这个是自动完成的），并向客户端发送一条消息：<code>[40001][1213] Deadlock found when trying to get lock; try restarting transaction</code> .</p>
<blockquote>
<p>那么如何定位到死锁的发生的地方呢？</p>
</blockquote>
<p>仍然是使用<code>show engine innnodb status</code>来查看死锁信息：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line">------------------------</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">最近一次捕捉的死锁信息</span></span><br><span class="line">LATEST DETECTED DEADLOCK</span><br><span class="line">------------------------</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">死锁发生时间</span></span><br><span class="line">2021-11-27 11:49:06 0x309c3d000</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">死锁发生时的第一个事务信息</span></span><br><span class="line">*** (1) TRANSACTION:</span><br><span class="line">TRANSACTION 7203086, ACTIVE 12 sec starting index read</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">该事务使用了一个表，为一个表上了锁</span></span><br><span class="line">mysql tables in use 1, locked 1</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">拥有 3 个锁结构，2个行锁</span></span><br><span class="line">LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s)</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">线程信息</span></span><br><span class="line">MySQL thread id 24, OS thread handle 13048426496, query id 978 localhost 127.0.0.1 root statistics</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">发生锁时执行的SQL语句</span></span><br><span class="line">/* ApplicationName=DataGrip 2020.3.1 */ select * from test_lock where primary_id = 20 for update</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">当前线程正在等待获取的锁</span></span><br><span class="line">*** (1) WAITING FOR THIS LOCK TO BE GRANTED:</span><br><span class="line">RECORD LOCKS space id 106 page no 4 n bits 80 index PRIMARY of table `test_lock`.`test_lock` trx id 7203086 lock_mode X locks rec but not gap waiting</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">这条记录正对应着 select * from test_lock <span class="built_in">where</span> primary_id = 20 <span class="keyword">for</span> update ;</span></span><br><span class="line">Record lock, heap no 4 PHYSICAL RECORD: n_fields 6; compact format; info bits 0</span><br><span class="line"> 0: len 4; hex 80000014; asc     ;;</span><br><span class="line"> 1: len 6; hex 0000002e0551; asc    . Q;;</span><br><span class="line"> 2: len 7; hex 010000012c112d; asc     , -;;</span><br><span class="line"> 3: len 4; hex 80000014; asc     ;;</span><br><span class="line"> 4: len 4; hex 80000014; asc     ;;</span><br><span class="line"> 5: len 4; hex 80000014; asc     ;;</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">死锁发生时的第二个事务信息</span></span><br><span class="line">*** (2) TRANSACTION:</span><br><span class="line">TRANSACTION 7203087, ACTIVE 9 sec starting index read</span><br><span class="line">mysql tables in use 1, locked 1</span><br><span class="line">3 lock struct(s), heap size 1136, 2 row lock(s)</span><br><span class="line">MySQL thread id 25, OS thread handle 13048729600, query id 987 localhost 127.0.0.1 root statistics</span><br><span class="line">/* ApplicationName=DataGrip 2020.3.1 */ select * from test_lock where primary_id = 10 for update</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">此事务已经或得到的锁</span></span><br><span class="line">*** (2) HOLDS THE LOCK(S):</span><br><span class="line">RECORD LOCKS space id 106 page no 4 n bits 80 index PRIMARY of table `test_lock`.`test_lock` trx id 7203087 lock_mode X locks rec but not gap</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">这条记录正对应着 select * from test_lock <span class="built_in">where</span> primary_id = 20 <span class="keyword">for</span> update ;</span></span><br><span class="line">Record lock, heap no 4 PHYSICAL RECORD: n_fields 6; compact format; info bits 0</span><br><span class="line"> 0: len 4; hex 80000014; asc     ;;</span><br><span class="line"> 1: len 6; hex 0000002e0551; asc    . Q;;</span><br><span class="line"> 2: len 7; hex 010000012c112d; asc     , -;;</span><br><span class="line"> 3: len 4; hex 80000014; asc     ;;</span><br><span class="line"> 4: len 4; hex 80000014; asc     ;;</span><br><span class="line"> 5: len 4; hex 80000014; asc     ;;</span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">此事务等待的锁</span></span><br><span class="line">*** (2) WAITING FOR THIS LOCK TO BE GRANTED:</span><br><span class="line">RECORD LOCKS space id 106 page no 4 n bits 80 index PRIMARY of table `test_lock`.`test_lock` trx id 7203087 lock_mode X locks rec but not gap waiting</span><br><span class="line">Record lock, heap no 3 PHYSICAL RECORD: n_fields 6; compact format; info bits 0</span><br><span class="line"> 0: len 4; hex 8000000a; asc     ;;</span><br><span class="line"> 1: len 6; hex 0000002e0502; asc    .  ;;</span><br><span class="line"> 2: len 7; hex 01000001641060; asc     d `;;</span><br><span class="line"> 3: len 4; hex 8000000a; asc     ;;</span><br><span class="line"> 4: len 4; hex 8000000a; asc     ;;</span><br><span class="line"> 5: len 4; hex 8000000a; asc     ;;</span><br><span class="line"><span class="meta prompt_"></span></span><br><span class="line"><span class="meta prompt_"># </span><span class="language-bash">InnoDB决定回滚第二个事务</span></span><br><span class="line">*** WE ROLL BACK TRANSACTION (2)</span><br></pre></td></tr></table></figure>



<p>不过注意，默认只记录最近一次死锁信息，可以将全局变量<code>innodb_print_all_deadlocks</code>设置为ON，这样的话</p>
<p>死锁发生的信息就会都记录在MySQL的错误日志中，然后就可以通过查看错误日志来分析更多的日志情况了。</p>
<p>于是，根据死锁发生的死锁日志来逆向定位产生的死锁的语句，然后再优化我们的业务。</p>
<h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><blockquote>
<p><a target="_blank" rel="noopener" href="https://draveness.me/database-concurrency-control/">浅谈数据库并发控制 - 锁和 MVCC</a></p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/11/03/%E5%B8%B8%E8%A7%81%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%AC%94%E8%AE%B0%EF%BC%88%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F%E3%80%81%E5%B8%8C%E5%B0%94%E6%8E%92%E5%BA%8F%E3%80%81%E5%BD%92%E5%B9%B6%E6%8E%92%E5%BA%8F%E3%80%81%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F%E3%80%81%E5%A0%86%E6%8E%92%E5%BA%8F%E3%80%81%E8%AE%A1%E6%95%B0%E6%8E%92%E5%BA%8F%E3%80%81%E5%9F%BA%E6%95%B0%E6%8E%92%E5%BA%8F%EF%BC%89/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/11/03/%E5%B8%B8%E8%A7%81%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%AC%94%E8%AE%B0%EF%BC%88%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F%E3%80%81%E5%B8%8C%E5%B0%94%E6%8E%92%E5%BA%8F%E3%80%81%E5%BD%92%E5%B9%B6%E6%8E%92%E5%BA%8F%E3%80%81%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F%E3%80%81%E5%A0%86%E6%8E%92%E5%BA%8F%E3%80%81%E8%AE%A1%E6%95%B0%E6%8E%92%E5%BA%8F%E3%80%81%E5%9F%BA%E6%95%B0%E6%8E%92%E5%BA%8F%EF%BC%89/" class="post-title-link" itemprop="url">常见排序算法笔记（插入排序、希尔排序、归并排序、快速排序、堆排序、计数排序、基数排序）</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-11-03 13:24:45" itemprop="dateCreated datePublished" datetime="2021-11-03T13:24:45+08:00">2021-11-03</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-01-03 20:28:07" itemprop="dateModified" datetime="2023-01-03T20:28:07+08:00">2023-01-03</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E7%AE%97%E6%B3%95/" itemprop="url" rel="index"><span itemprop="name">算法</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <blockquote>
<p>这篇博客主要梳理了一些排序算法，为了测试算法的正确性，于是选择了力扣上的一道裸题去测试。算法的正确性相对来讲可以保证。</p>
</blockquote>
<h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p><img src="/2021/11/03/%E5%B8%B8%E8%A7%81%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%AC%94%E8%AE%B0%EF%BC%88%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F%E3%80%81%E5%B8%8C%E5%B0%94%E6%8E%92%E5%BA%8F%E3%80%81%E5%BD%92%E5%B9%B6%E6%8E%92%E5%BA%8F%E3%80%81%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F%E3%80%81%E5%A0%86%E6%8E%92%E5%BA%8F%E3%80%81%E8%AE%A1%E6%95%B0%E6%8E%92%E5%BA%8F%E3%80%81%E5%9F%BA%E6%95%B0%E6%8E%92%E5%BA%8F%EF%BC%89/%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95%E7%A8%B3%E5%AE%9A%E6%80%A7.jpg"></p>
<ul>
<li>[LeetCode912. 排序数组]<br> <a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/sort-an-array/">传送门</a></li>
</ul>
<h2 id="插入排序"><a href="#插入排序" class="headerlink" title="插入排序"></a>插入排序</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">insertion_sort</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; nums)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> j=<span class="number">1</span>;j&lt;nums.<span class="built_in">size</span>();j++)&#123;</span><br><span class="line">            <span class="type">int</span> i = j<span class="number">-1</span>;</span><br><span class="line">            <span class="type">int</span> key = nums[j];</span><br><span class="line">            <span class="keyword">while</span>(i&gt;=<span class="number">0</span> &amp;&amp; nums[i]&gt;key)&#123;</span><br><span class="line">                nums[i+<span class="number">1</span>] = nums[i];</span><br><span class="line">                i--;</span><br><span class="line">            &#125;</span><br><span class="line">            nums[i+<span class="number">1</span>] = key;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> nums;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>


<h2 id="希尔排序"><a href="#希尔排序" class="headerlink" title="希尔排序"></a>希尔排序</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">shell_sort</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; nums)</span> </span>&#123;</span><br><span class="line">        <span class="type">int</span> n = nums.<span class="built_in">size</span>();</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> gap = n/<span class="number">2</span>;gap&gt;=<span class="number">1</span>;gap/=<span class="number">2</span>)&#123;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> j=gap;j&lt;nums.<span class="built_in">size</span>();j++)&#123;</span><br><span class="line">                <span class="type">int</span> i = j-gap;</span><br><span class="line">                <span class="type">int</span> key = nums[j];</span><br><span class="line">                <span class="keyword">while</span>(i&gt;=<span class="number">0</span> &amp;&amp; nums[i]&gt;key)&#123;</span><br><span class="line">                    nums[i+gap] = nums[i];</span><br><span class="line">                    i -= gap;</span><br><span class="line">                &#125;</span><br><span class="line">                nums[i+gap] = key;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> nums;   </span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h2 id="归并排序"><a href="#归并排序" class="headerlink" title="归并排序"></a>归并排序</h2><ul>
<li><p><strong>递归写法</strong></p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">sortArray</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; nums)</span> </span>&#123;</span><br><span class="line">        <span class="built_in">merge_sort</span>(nums,<span class="number">0</span>,nums.<span class="built_in">size</span>()<span class="number">-1</span>);</span><br><span class="line">        <span class="keyword">return</span> nums;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">merge_sort</span><span class="params">(vector&lt;<span class="type">int</span>&gt; &amp;nums, <span class="type">int</span> l, <span class="type">int</span> r)</span></span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(l&gt;=r) <span class="keyword">return</span>;</span><br><span class="line">        <span class="type">int</span> mid = l+(r-l)/<span class="number">2</span>;</span><br><span class="line">        <span class="built_in">merge_sort</span>(nums,l,mid);</span><br><span class="line">        <span class="built_in">merge_sort</span>(nums,mid+<span class="number">1</span>,r);</span><br><span class="line">        <span class="built_in">helper_merge</span>(nums,l,r);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">helper_merge</span><span class="params">(vector&lt;<span class="type">int</span>&gt; &amp;nums, <span class="type">int</span> l, <span class="type">int</span> r)</span></span>&#123;</span><br><span class="line">        vector&lt;<span class="type">int</span>&gt; temp;</span><br><span class="line">        <span class="type">int</span> mid = l+(r-l)/<span class="number">2</span>;</span><br><span class="line">        <span class="type">int</span> i = l;</span><br><span class="line">        <span class="type">int</span> j = mid+<span class="number">1</span>;</span><br><span class="line">        <span class="keyword">while</span>(i&lt;=mid &amp;&amp; j&lt;=r)&#123;</span><br><span class="line">            <span class="keyword">if</span>(nums[i]&lt;=nums[j]) temp.<span class="built_in">push_back</span>(nums[i++]);</span><br><span class="line">            <span class="keyword">else</span> temp.<span class="built_in">push_back</span>(nums[j++]);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">while</span>(i&lt;=mid) temp.<span class="built_in">push_back</span>(nums[i++]);</span><br><span class="line">        <span class="keyword">while</span>(j&lt;=r) temp.<span class="built_in">push_back</span>(nums[j++]);</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=l;i&lt;=r;i++)&#123;</span><br><span class="line">            nums[i] = temp[i-l];</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
</li>
<li><p><strong>迭代写法</strong></p>
</li>
</ul>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="type">int</span> maxIndex;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">sortArray</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; nums)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="type">int</span> gap = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">        maxIndex = nums.<span class="built_in">size</span>() - <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">while</span>(gap &lt; nums.<span class="built_in">size</span>())&#123;</span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> l = <span class="number">0</span>;l&lt;maxIndex;l += <span class="number">2</span>*gap)&#123;</span><br><span class="line">                <span class="type">int</span> r = maxIndex-l+<span class="number">1</span>;</span><br><span class="line">                <span class="comment">// 如果剩下的元素小于等于 gap，那么在上一轮已经排好了，跳过</span></span><br><span class="line">                <span class="keyword">if</span>(r &lt;= gap) <span class="keyword">continue</span>;</span><br><span class="line">                <span class="built_in">merge</span>(nums,l,gap);</span><br><span class="line">            &#125;</span><br><span class="line">            gap *= <span class="number">2</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> nums;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">merge</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; nums,<span class="type">int</span> l1,<span class="type">int</span> gap)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">        vector&lt;<span class="type">int</span>&gt; temp;</span><br><span class="line">        <span class="type">int</span> r1 = l1+gap<span class="number">-1</span>;</span><br><span class="line">        <span class="type">int</span> l2 = r1+<span class="number">1</span>;</span><br><span class="line">        <span class="type">int</span> r2 = <span class="built_in">min</span>(maxIndex,l2+gap<span class="number">-1</span>);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="comment">// 双指针合并</span></span><br><span class="line">        <span class="type">int</span> i = l1, j = l2;</span><br><span class="line">        <span class="keyword">while</span>(i &lt;= r1 &amp;&amp; j &lt;= r2)&#123;</span><br><span class="line">            <span class="keyword">if</span>(nums[i] &lt; nums[j])&#123;</span><br><span class="line">                temp.<span class="built_in">push_back</span>(nums[i++]);</span><br><span class="line">            &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">                temp.<span class="built_in">push_back</span>(nums[j++]);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">while</span>(i &lt;= r1) temp.<span class="built_in">push_back</span>(nums[i++]);        </span><br><span class="line">        <span class="keyword">while</span>(j &lt;= r2) temp.<span class="built_in">push_back</span>(nums[j++]);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="type">int</span> pos = l1;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> x:temp)&#123;</span><br><span class="line">            nums[pos++] = x;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h2 id="快速排序"><a href="#快速排序" class="headerlink" title="快速排序"></a>快速排序</h2><p>快排的最核心的地方就是如何根据一个基准值将一个数组分成两部分。</p>
<p>yxc 版本</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.*;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 代码中的类名、方法名、参数名已经指定，请勿修改，直接返回方法规定的值即可</span></span><br><span class="line"><span class="comment">     * 将给定数组排序</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> arr int整型一维数组 待排序的数组</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> int整型一维数组</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span>[] MySort (<span class="type">int</span>[] arr) &#123;</span><br><span class="line">        <span class="comment">// write code here</span></span><br><span class="line">        qsort(arr, <span class="number">0</span> , arr.length - <span class="number">1</span>);</span><br><span class="line">        <span class="keyword">return</span> arr;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">qsort</span><span class="params">(<span class="type">int</span>[] a, <span class="type">int</span> l, <span class="type">int</span> r)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span>(l &gt;= r) <span class="keyword">return</span>;</span><br><span class="line">        <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> l - <span class="number">1</span>, j = r + <span class="number">1</span>, x = a[(l + r)/<span class="number">2</span>];</span><br><span class="line">        <span class="keyword">while</span>(i &lt; j) &#123;</span><br><span class="line">            <span class="keyword">do</span> &#123;i++; &#125; <span class="keyword">while</span>(a[i] &lt; x);</span><br><span class="line">            <span class="keyword">do</span> &#123;j--; &#125; <span class="keyword">while</span>(a[j] &gt; x);</span><br><span class="line">            <span class="keyword">if</span>(i &lt; j) &#123;</span><br><span class="line">                <span class="type">int</span> <span class="variable">temp</span> <span class="operator">=</span> a[i];</span><br><span class="line">                a[i] = a[j];</span><br><span class="line">                a[j] = temp;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 此时 i == j 或者 i &gt; j, a[j] &lt;= x (基准值)</span></span><br><span class="line">        <span class="comment">// 注意是 j</span></span><br><span class="line">        qsort(a, l, j);</span><br><span class="line">        qsort(a, j + <span class="number">1</span>, r);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<ul>
<li><p><strong>《啊哈算法》的版本(双向指针)</strong></p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">sortArray</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; nums)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(nums.<span class="built_in">size</span>()&lt;=<span class="number">1</span>) <span class="keyword">return</span> nums;</span><br><span class="line">        <span class="built_in">quick_sort</span>(nums,<span class="number">0</span>,nums.<span class="built_in">size</span>()<span class="number">-1</span>);</span><br><span class="line">        <span class="keyword">return</span> nums;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">quick_sort</span><span class="params">(vector&lt;<span class="type">int</span>&gt; &amp;nums,<span class="type">int</span> l,<span class="type">int</span> r)</span></span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(l&gt;=r) <span class="keyword">return</span>;</span><br><span class="line">        <span class="type">int</span> base = nums[l];</span><br><span class="line">        <span class="type">int</span> i = l , j = r;</span><br><span class="line">        <span class="keyword">while</span>(i&lt;j)&#123;</span><br><span class="line">            <span class="keyword">while</span>(i&lt;j &amp;&amp; nums[j]&gt;=base) j--;</span><br><span class="line">            <span class="keyword">while</span>(i&lt;j &amp;&amp; nums[i]&lt;=base) i++;</span><br><span class="line">            <span class="built_in">swap</span>(nums[i],nums[j]);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 基准值归位</span></span><br><span class="line">        <span class="comment">// 此时 i==j 并且 nums[i]&lt;base 因为是j指针先移动的</span></span><br><span class="line">        nums[l] = nums[i];</span><br><span class="line">        nums[i] = base;</span><br><span class="line">        <span class="built_in">quick_sort</span>(nums,l,i<span class="number">-1</span>);</span><br><span class="line">        <span class="built_in">quick_sort</span>(nums,i+<span class="number">1</span>,r);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>
</li>
<li><p><strong>《算法导论》的版本(同向指针)</strong></p>
<ul>
<li><strong>循环不变量法</strong><br>  [l,i] 都小于等于x,<br>   [i+1,j] 都大于x，<br>   [j+1,r-1] 尚未处理，<br>   nums[r] 是基准值。</li>
</ul>
</li>
</ul>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">sortArray</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; nums)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(nums.<span class="built_in">size</span>()&lt;=<span class="number">1</span>) <span class="keyword">return</span> nums;</span><br><span class="line">        <span class="built_in">quick_sort</span>(nums,<span class="number">0</span>,nums.<span class="built_in">size</span>()<span class="number">-1</span>);</span><br><span class="line">        <span class="keyword">return</span> nums;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">quick_sort</span><span class="params">(vector&lt;<span class="type">int</span>&gt; &amp;nums,<span class="type">int</span> l,<span class="type">int</span> r)</span></span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(l&gt;=r) <span class="keyword">return</span>;</span><br><span class="line">        <span class="type">int</span> p = <span class="built_in">partion</span>(nums,l,r);</span><br><span class="line">        <span class="built_in">quick_sort</span>(nums,l,p<span class="number">-1</span>);</span><br><span class="line">        <span class="built_in">quick_sort</span>(nums,p+<span class="number">1</span>,r);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 循环不变量 [l,i] 都小于等于x,</span></span><br><span class="line">    <span class="comment">// [i+1,j] 都大于x</span></span><br><span class="line">    <span class="comment">// [j+1,r-1] 尚未处理</span></span><br><span class="line">    <span class="comment">// nums[r] 是基准值</span></span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">partion</span><span class="params">(vector&lt;<span class="type">int</span>&gt; &amp;nums,<span class="type">int</span> l,<span class="type">int</span> r)</span></span>&#123;</span><br><span class="line">        <span class="type">int</span> x = nums[r];</span><br><span class="line">        <span class="type">int</span> i = l<span class="number">-1</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> j=l;j&lt;r;j++)&#123;</span><br><span class="line">            <span class="keyword">if</span>(nums[j]&lt;=x)&#123;</span><br><span class="line">            	i++;</span><br><span class="line">            	<span class="comment">// 此时nums[i]大于x,而nums[j]小于等于x</span></span><br><span class="line">            	<span class="comment">// 一交换 循环不变量保持</span></span><br><span class="line">                <span class="built_in">swap</span>(nums[j],nums[i]);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">swap</span>(nums[i+<span class="number">1</span>],nums[r]);</span><br><span class="line">        <span class="keyword">return</span> i+<span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>


<ul>
<li><strong>随机化版本也很简单</strong></li>
</ul>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">sortArray</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; nums)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(nums.<span class="built_in">size</span>()&lt;=<span class="number">1</span>) <span class="keyword">return</span> nums;</span><br><span class="line">        <span class="built_in">srand</span>((<span class="type">unsigned</span>)<span class="built_in">time</span>(<span class="literal">NULL</span>));</span><br><span class="line">        <span class="built_in">randomed_quick_sort</span>(nums,<span class="number">0</span>,nums.<span class="built_in">size</span>()<span class="number">-1</span>);</span><br><span class="line">        <span class="keyword">return</span> nums;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">randomed_quick_sort</span><span class="params">(vector&lt;<span class="type">int</span>&gt; &amp;nums,<span class="type">int</span> l,<span class="type">int</span> r)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(l&gt;=r) <span class="keyword">return</span>;</span><br><span class="line">        <span class="type">int</span> i = <span class="built_in">rand</span>() % (r - l + <span class="number">1</span>) + l; <span class="comment">// 随机选一个作为我们的主元 [0,r-l]+l = [l,r]</span></span><br><span class="line">        <span class="built_in">swap</span>(nums[i],nums[r]);</span><br><span class="line">        <span class="type">int</span> p = <span class="built_in">partion</span>(nums,l,r);</span><br><span class="line">        <span class="built_in">randomed_quick_sort</span>(nums,l,p<span class="number">-1</span>);</span><br><span class="line">        <span class="built_in">randomed_quick_sort</span>(nums,p+<span class="number">1</span>,r);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 循环不变量 [l,i] 都小于等于x,</span></span><br><span class="line">    <span class="comment">// [i+1,j] 都大于x</span></span><br><span class="line">    <span class="comment">// [j+1,r-1] 尚未处理</span></span><br><span class="line">    <span class="comment">// nums[r] 是基准值</span></span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">partion</span><span class="params">(vector&lt;<span class="type">int</span>&gt; &amp;nums,<span class="type">int</span> l,<span class="type">int</span> r)</span> </span>&#123;</span><br><span class="line">        <span class="type">int</span> x = nums[r];</span><br><span class="line">        <span class="type">int</span> i = l<span class="number">-1</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> j=l;j&lt;r;j++)&#123;</span><br><span class="line">            <span class="keyword">if</span>(nums[j]&lt;=x)&#123;</span><br><span class="line">            	i++;</span><br><span class="line">            	<span class="comment">// 此时nums[i]大于x,而nums[j]小于等于x</span></span><br><span class="line">            	<span class="comment">// 一交换 循环不变量保持</span></span><br><span class="line">                <span class="built_in">swap</span>(nums[j],nums[i]);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">swap</span>(nums[i+<span class="number">1</span>],nums[r]);</span><br><span class="line">        <span class="keyword">return</span> i+<span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h2 id="堆排序"><a href="#堆排序" class="headerlink" title="堆排序"></a>堆排序</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="type">int</span> heap[<span class="number">50010</span>], size;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">down</span><span class="params">(<span class="type">int</span> x)</span></span>&#123;</span><br><span class="line">        <span class="type">int</span> least = x;</span><br><span class="line">        <span class="type">int</span> l = <span class="number">2</span>*x;</span><br><span class="line">        <span class="type">int</span> r = <span class="number">2</span>*x+<span class="number">1</span>;</span><br><span class="line">        <span class="keyword">if</span>(l&lt;=size &amp;&amp; heap[l]&lt;heap[least]) least = l;</span><br><span class="line">        <span class="keyword">if</span>(r&lt;=size &amp;&amp; heap[r]&lt;heap[least]) least = r;</span><br><span class="line">        <span class="keyword">if</span>(least!=x)&#123;</span><br><span class="line">            <span class="built_in">swap</span>(heap[x],heap[least]);</span><br><span class="line">            <span class="built_in">down</span>(least);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">	<span class="comment">// 建堆 </span></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">build_heap</span><span class="params">(<span class="type">const</span> vector&lt;<span class="type">int</span>&gt;&amp; nums)</span></span>&#123;</span><br><span class="line">        size = nums.<span class="built_in">size</span>();</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;size;i++) heap[i+<span class="number">1</span>] = nums[i];</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=size/<span class="number">2</span>;i&gt;=<span class="number">1</span>;i--)&#123;</span><br><span class="line">            <span class="built_in">down</span>(i);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">	<span class="comment">// 依次取出堆顶，然后调整堆。</span></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">heap_sort</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; nums)</span></span>&#123;</span><br><span class="line">        <span class="type">int</span> pos = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">while</span>(size)&#123;</span><br><span class="line">            nums[pos++] = heap[<span class="number">1</span>];</span><br><span class="line">            heap[<span class="number">1</span>] = heap[size--];</span><br><span class="line">            <span class="built_in">down</span>(<span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">sortArray</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; nums)</span> </span>&#123;</span><br><span class="line">        <span class="built_in">build_heap</span>(nums);</span><br><span class="line">        <span class="built_in">heap_sort</span>(nums);</span><br><span class="line">        <span class="keyword">return</span> nums;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>


<h2 id="计数排序"><a href="#计数排序" class="headerlink" title="计数排序"></a>计数排序</h2><p>力扣的数据：$[-50000,50000]$，用一个线性函数将所有元素映射到区间$[0,100000]$</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="type">static</span> <span class="type">const</span> <span class="type">int</span> N = <span class="number">100010</span>;</span><br><span class="line">    <span class="type">int</span> bin[N] = &#123;<span class="number">0</span>&#125;;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">sortArray</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; nums)</span> </span>&#123;</span><br><span class="line">        vector&lt;<span class="type">int</span>&gt; ans;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> x:nums)&#123;</span><br><span class="line">            bin[<span class="built_in">f</span>(x)] ++;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;N;i++)&#123;</span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> j=<span class="number">0</span>;j&lt;bin[i];j++)&#123;</span><br><span class="line">                ans.<span class="built_in">push_back</span>(<span class="built_in">g</span>(i));</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">f</span><span class="params">(<span class="type">int</span> num)</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> num + <span class="number">50000</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">g</span><span class="params">(<span class="type">int</span> id)</span></span>&#123;</span><br><span class="line">        <span class="keyword">return</span> id - <span class="number">50000</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p><strong>海量考试分数排序</strong></p>
<p>一年的全国高考考生人数为500 万，分数使用标准分，最低100 ，最高900 ，没有小数，要求对这500 万元素的数组进行排序。</p>
<p>分析：对500W数据排序，如果基于比较的先进排序，平均比较次数为O(5000000*log5000000)≈1.112亿。但是我们发现，这些数据都有特殊的条件： 100&#x3D;&lt;score&lt;&#x3D;900。那么我们就可以考虑桶排序这样一个“投机取巧”的办法、让其在毫秒级别就完成500万排序。</p>
<p>方法：创建801(900-100)个桶。将每个考生的分数丢进f(score)&#x3D;score-100的桶中。这个过程从头到尾遍历一遍数据只需要500W次。然后根据桶号大小依次将桶中数值输出，即可以得到一个有序的序列。而且可以很容易的得到100分有人，501分有人。</p>
<p>实际上，桶排序对数据的条件有特殊要求，如果上面的分数不是从100-900，而是从0-2亿，那么分配2亿个桶显然是不可能的。所以桶排序有其局限性，适合元素值集合并不大的情况。</p>
<h2 id="基数排序"><a href="#基数排序" class="headerlink" title="基数排序"></a>基数排序</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">sortByKth</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; nums,<span class="type">int</span> k)</span> </span>&#123;</span><br><span class="line">        vector&lt;<span class="type">int</span>&gt; bin[<span class="number">10</span>];</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> x:nums)&#123;</span><br><span class="line">            bin[ <span class="built_in">f</span>(x,k) ].<span class="built_in">push_back</span>(x);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span> pos = <span class="number">0</span>;  </span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i&lt;<span class="number">10</span>;i++)&#123;</span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> j=<span class="number">0</span>;j&lt;bin[i].<span class="built_in">size</span>();j++)&#123;</span><br><span class="line">                nums[pos++] = bin[i][j];</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">f</span><span class="params">(<span class="type">int</span> num,<span class="type">int</span> k)</span></span>&#123;</span><br><span class="line">        <span class="keyword">while</span>(k--)&#123;</span><br><span class="line">            num /= <span class="number">10</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> num % <span class="number">10</span>;</span><br><span class="line">    &#125; </span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">sortArray</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; nums)</span></span>&#123;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> &amp;x:nums) x += <span class="number">50000</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> k=<span class="number">0</span>;k&lt;=<span class="number">5</span>;k++)&#123;</span><br><span class="line">            <span class="built_in">sortByKth</span>(nums,k);</span><br><span class="line">        &#125;</span><br><span class="line">        vector&lt;<span class="type">int</span>&gt; ans;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> &amp;x:nums) x -= <span class="number">50000</span>;</span><br><span class="line">        <span class="keyword">return</span> nums;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>



<h2 id="桶排序"><a href="#桶排序" class="headerlink" title="桶排序"></a>桶排序</h2><p><strong>桶排序（Bucket sort）</strong>或所谓的<strong>箱排序</strong>，是一个排序算法，工作的原理是将数组分到有限数量的桶里。每个桶再个别排序（有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序）。当要被排序的数组内的数值是均匀分配的时候，桶排序使用线性时间O(n)。但桶排序并不是比较排序，他不受到O(n log n)下限的影响。</p>
<p>桶排序以下列程序进行：</p>
<ol>
<li>设置一个定量的数组当作空桶子。</li>
<li>寻访序列，并且把项目一个一个放到对应的桶子去。</li>
<li>对每个不是空的桶子进行排序。</li>
<li>从不是空的桶子里把项目再放回原来的序列中。</li>
</ol>
<h3 id="桶排序的应用"><a href="#桶排序的应用" class="headerlink" title="桶排序的应用"></a>桶排序的应用</h3><p><strong>在一个文件中有10G个整数，乱序排列，要求找出中位数。内存限制为2G。</strong><br>只写出思路即可（内存限制为2G意思是可以使用2G空间来运行程序，而不考虑本机上其他软件内存占用情况。）</p>
<p> 关于中位数：数据排序后，位置在最中间的数值。即将数据分成两部分，一部分大于该数值，一部分小于该数值。中位数的位置：当样本数为奇数时，中位数&#x3D;(N+1)&#x2F;2 ; 当样本数为偶数时，中位数为N&#x2F;2与1+N&#x2F;2的均值（那么10G个数的中位数，就第5G大的数与第5G+1大的数的均值了）。</p>
<p>分析：既然要找中位数，很简单就是排序的想法。那么基于字节的桶排序是一个可行的方法。</p>
<p>思想：将整型的每1byte作为一个关键字，也就是说一个整形可以拆成4个keys，而且最高位的keys越大，整数越大。如果高位keys相同，则比较次高位的keys。整个比较过程类似于字符串的字典序。</p>
<p>第一步:把10G整数每2G读入一次内存，然后一次遍历这536,870,912即（1024<em>1024</em>1024）*2 &#x2F;4个数据。每个数据用位运算”&gt;&gt;”取出最高8位(31-24)。这8bits(0-255)最多表示256个桶，那么可以根据8bit的值来确定丢入第几个桶。最后把每个桶写入一个磁盘文件中，同时在内存中统计每个桶内数据的数量NUM[256]。</p>
<p>代价：(1) 10G数据依次读入内存的IO代价(这个是无法避免的，CPU不能直接在磁盘上运算)。(2)在内存中遍历536,870,912个数据，这是一个O(n)的线性时间复杂度。(3)把256个桶写回到256个磁盘文件空间中，这个代价是额外的，也就是多付出一倍的10G数据转移的时间。</p>
<p>第二步：根据内存中256个桶内的数量NUM[256]，计算中位数在第几个桶中。很显然，2,684,354,560个数中位数是第1,342,177,280个。假设前127个桶的数据量相加，发现少于1,342,177,280，把第128个桶数据量加上，大于1,342,177,280。说明，中位数必在磁盘的第128个桶中。而且在这个桶的第1,342,177,280-N(0-127)个数位上。N(0-127)表示前127个桶的数据量之和。然后把第128个文件中的整数读入内存。(若数据大致是均匀分布的，每个文件的大小估计在10G&#x2F;256&#x3D;40M左右，当然也不一定，但是超过2G的可能性很小)。注意，变态的情况下，这个需要读入的第128号文件仍然大于2G，那么整个读入仍然可以按照第一步分批来进行读取。</p>
<p>代价：(1)循环计算255个桶中的数据量累加，需要O(M)的代价，其中m&lt;255。(2)读入一个大概80M左右文件大小的IO代价。</p>
<p>第三步：继续以内存中的某个桶内整数的次高8bit（他们的最高8bit是一样的）进行桶排序(23-16)。过程和第一步相同，也是256个桶。</p>
<p>第四步：一直下去，直到最低字节(7-0bit)的桶排序结束。我相信这个时候完全可以在内存中使用一次快排就可以了。</p>
<p>整个过程的时间复杂度在O(n)的线性级别上(没有任何循环嵌套)。但主要时间消耗在第一步的第二次内存-磁盘数据交换上，即10G数据分255个文件写回磁盘上。一般而言，如果第二步过后，内存可以容纳下存在中位数的某一个文件的话，直接快排就可以了（修改者注：我想，继续桶排序但不写回磁盘，效率会更高？）。  </p>
<blockquote>
<p>参考</p>
<p><a target="_blank" rel="noopener" href="https://baike.baidu.com/item/%E6%A1%B6%E6%8E%92%E5%BA%8F/4973777">百度百科-桶排序</a></p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/11/02/%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E5%90%84%E7%A7%8D%E9%81%8D%E5%8E%86%EF%BC%88%E9%80%92%E5%BD%92%E4%B8%8E%E8%BF%AD%E4%BB%A3%E5%AE%9E%E7%8E%B0%EF%BC%89%E4%BB%A5%E5%8F%8A%E4%BD%BF%E7%94%A8%E4%B8%AD%E5%BA%8F%E3%80%81%EF%BC%88%E5%89%8D%E5%BA%8F-%E5%90%8E%E5%BA%8F%EF%BC%89%E8%BF%98%E5%8E%9F%E4%BA%8C%E5%8F%89%E6%A0%91/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/11/02/%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E5%90%84%E7%A7%8D%E9%81%8D%E5%8E%86%EF%BC%88%E9%80%92%E5%BD%92%E4%B8%8E%E8%BF%AD%E4%BB%A3%E5%AE%9E%E7%8E%B0%EF%BC%89%E4%BB%A5%E5%8F%8A%E4%BD%BF%E7%94%A8%E4%B8%AD%E5%BA%8F%E3%80%81%EF%BC%88%E5%89%8D%E5%BA%8F-%E5%90%8E%E5%BA%8F%EF%BC%89%E8%BF%98%E5%8E%9F%E4%BA%8C%E5%8F%89%E6%A0%91/" class="post-title-link" itemprop="url">二叉树的各种遍历（递归与迭代实现）以及使用中序、（前序 | 后序）还原二叉树</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-11-02 23:58:55" itemprop="dateCreated datePublished" datetime="2021-11-02T23:58:55+08:00">2021-11-02</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-01-03 20:25:00" itemprop="dateModified" datetime="2023-01-03T20:25:00+08:00">2023-01-03</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/" itemprop="url" rel="index"><span itemprop="name">数据结构</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <blockquote>
<p>这里就用力扣上面的各种题目要模板了。</p>
</blockquote>
<h1 id="二叉树的遍历"><a href="#二叉树的遍历" class="headerlink" title="二叉树的遍历"></a>二叉树的遍历</h1><h2 id="层次遍历"><a href="#层次遍历" class="headerlink" title="层次遍历"></a>层次遍历</h2><p><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/binary-tree-level-order-traversal/submissions/">102. 二叉树的层序遍历</a></p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    vector&lt;vector&lt;<span class="type">int</span>&gt;&gt; <span class="built_in">levelOrder</span>(TreeNode* root) &#123;</span><br><span class="line">        vector&lt;vector&lt;<span class="type">int</span>&gt;&gt; ans;</span><br><span class="line">        <span class="keyword">if</span>(!root) <span class="keyword">return</span> ans;</span><br><span class="line">        queue&lt;TreeNode*&gt; q;</span><br><span class="line">        q.<span class="built_in">push</span>(root);</span><br><span class="line">        <span class="keyword">while</span>(!q.<span class="built_in">empty</span>())&#123;</span><br><span class="line">            <span class="type">int</span> size = q.<span class="built_in">size</span>();</span><br><span class="line">            vector&lt;<span class="type">int</span>&gt; list;</span><br><span class="line">            <span class="keyword">while</span>(size--)&#123;</span><br><span class="line">                TreeNode* r =  q.<span class="built_in">front</span>();</span><br><span class="line">                q.<span class="built_in">pop</span>();</span><br><span class="line">                list.<span class="built_in">push_back</span>(r-&gt;val);</span><br><span class="line">                <span class="keyword">if</span>(r-&gt;left) q.<span class="built_in">push</span>(r-&gt;left);</span><br><span class="line">                <span class="keyword">if</span>(r-&gt;right) q.<span class="built_in">push</span>(r-&gt;right);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span>(list.<span class="built_in">size</span>())&#123;</span><br><span class="line">                ans.<span class="built_in">push_back</span>(list);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h2 id="前序遍历、中序遍历、后序遍历的递归写法"><a href="#前序遍历、中序遍历、后序遍历的递归写法" class="headerlink" title="前序遍历、中序遍历、后序遍历的递归写法"></a>前序遍历、中序遍历、后序遍历的递归写法</h2><p><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/binary-tree-preorder-traversal/">144. 二叉树的前序遍历</a><br><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/binary-tree-inorder-traversal/">94. 二叉树的中序遍历</a><br><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/binary-tree-postorder-traversal/">145. 二叉树的后序遍历</a></p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 先序</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    vector&lt;<span class="type">int</span>&gt; ans;</span><br><span class="line">    <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">preorderTraversal</span><span class="params">(TreeNode* root)</span> </span>&#123;</span><br><span class="line">        <span class="built_in">preOrder</span>(root);</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">preOrder</span><span class="params">(TreeNode* root)</span></span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(!root) <span class="keyword">return</span>;</span><br><span class="line">        ans.<span class="built_in">push_back</span>(root-&gt;val);</span><br><span class="line">        <span class="built_in">preOrder</span>(root-&gt;left);</span><br><span class="line">        <span class="built_in">preOrder</span>(root-&gt;right);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 中序</span></span><br><span class="line">​```cpp</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    vector&lt;<span class="type">int</span>&gt; ans;</span><br><span class="line">    <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">inorderTraversal</span><span class="params">(TreeNode* root)</span> </span>&#123;</span><br><span class="line">        <span class="built_in">inOrder</span>(root);</span><br><span class="line">        <span class="keyword">return</span> ans;    </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">inOrder</span><span class="params">(TreeNode* root)</span></span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(!root) <span class="keyword">return</span>;</span><br><span class="line">        <span class="built_in">inOrder</span>(root-&gt;left);</span><br><span class="line">        ans.<span class="built_in">push_back</span>(root-&gt;val);</span><br><span class="line">        <span class="built_in">inOrder</span>(root-&gt;right);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 后序</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    vector&lt;<span class="type">int</span>&gt; ans;</span><br><span class="line">    <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">postorderTraversal</span><span class="params">(TreeNode* root)</span> </span>&#123;</span><br><span class="line">        <span class="built_in">postorder</span>(root);</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">postorder</span><span class="params">(TreeNode* root)</span></span>&#123;</span><br><span class="line">    	<span class="keyword">if</span>(!root) <span class="keyword">return</span>;</span><br><span class="line">        <span class="built_in">postorder</span>(root-&gt;left);</span><br><span class="line">        <span class="built_in">postorder</span>(root-&gt;right);</span><br><span class="line">        ans.<span class="built_in">push_back</span>(root-&gt;val);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br></pre></td></tr></table></figure>



<h2 id="前序遍历、中序遍历、后序遍历的迭代写法"><a href="#前序遍历、中序遍历、后序遍历的迭代写法" class="headerlink" title="前序遍历、中序遍历、后序遍历的迭代写法"></a>前序遍历、中序遍历、后序遍历的迭代写法</h2><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 前序遍历最好写</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    vector&lt;<span class="type">int</span>&gt; ans;</span><br><span class="line">    <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">preorderTraversal</span><span class="params">(TreeNode* root)</span> </span>&#123;</span><br><span class="line">        stack&lt;TreeNode*&gt; st;</span><br><span class="line">        <span class="keyword">if</span>(root) st.<span class="built_in">push</span>(root);</span><br><span class="line">        <span class="keyword">while</span>(!st.<span class="built_in">empty</span>())&#123;</span><br><span class="line">            TreeNode* p = st.<span class="built_in">top</span>();</span><br><span class="line">            st.<span class="built_in">pop</span>();</span><br><span class="line">            ans.<span class="built_in">push_back</span>(p-&gt;val);</span><br><span class="line">            <span class="keyword">if</span>(p-&gt;right) st.<span class="built_in">push</span>(p-&gt;right);</span><br><span class="line">            <span class="keyword">if</span>(p-&gt;left) st.<span class="built_in">push</span>(p-&gt;left);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 中序遍历</span></span><br><span class="line"><span class="comment">// 左 根 右，所以一直往左走，将经过的节点入栈，直到没有左孩子，然后输出现在的根，然后处理右子树。</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    vector&lt;<span class="type">int</span>&gt; ans;</span><br><span class="line">    <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">inorderTraversal</span><span class="params">(TreeNode* root)</span> </span>&#123;</span><br><span class="line">        stack&lt;TreeNode*&gt; st;</span><br><span class="line">        <span class="keyword">while</span>(root || st.<span class="built_in">size</span>())&#123;</span><br><span class="line">            <span class="keyword">while</span>(root)&#123;</span><br><span class="line">                st.<span class="built_in">push</span>(root);</span><br><span class="line">                root = root-&gt;left;</span><br><span class="line">            &#125;</span><br><span class="line">            root = st.<span class="built_in">top</span>();</span><br><span class="line">            st.<span class="built_in">pop</span>();</span><br><span class="line">            ans.<span class="built_in">push_back</span>(root-&gt;val);</span><br><span class="line">            root = root-&gt;right;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ans; </span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 更加直观的标记法</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    vector&lt;<span class="type">int</span>&gt; ans;</span><br><span class="line">    <span class="keyword">typedef</span> pair&lt;TreeNode*,<span class="type">bool</span>&gt; P;</span><br><span class="line">    <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">inorderTraversal</span><span class="params">(TreeNode* root)</span> </span>&#123;</span><br><span class="line">        stack&lt;P&gt; st;</span><br><span class="line">        <span class="keyword">if</span>(root) st.<span class="built_in">push</span>(&#123;root,<span class="literal">false</span>&#125;);</span><br><span class="line">        <span class="keyword">while</span>(!st.<span class="built_in">empty</span>())&#123;</span><br><span class="line">            P p = st.<span class="built_in">top</span>();</span><br><span class="line">            st.<span class="built_in">pop</span>();</span><br><span class="line">            <span class="keyword">if</span>(p.second)&#123;</span><br><span class="line">                ans.<span class="built_in">push_back</span>(p.first-&gt;val);</span><br><span class="line">            &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">                p.second = <span class="literal">true</span>;</span><br><span class="line">                <span class="keyword">if</span>(p.first-&gt;right) st.<span class="built_in">push</span>(&#123;p.first-&gt;right,<span class="literal">false</span>&#125;);</span><br><span class="line">                st.<span class="built_in">push</span>(p);</span><br><span class="line">                <span class="keyword">if</span>(p.first-&gt;left) st.<span class="built_in">push</span>(&#123;p.first-&gt;left,<span class="literal">false</span>&#125;);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// 后序遍历</span></span><br><span class="line"><span class="comment">// 直观的标记法</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    vector&lt;<span class="type">int</span>&gt; ans;</span><br><span class="line">    <span class="keyword">typedef</span> pair&lt;TreeNode*,<span class="type">bool</span>&gt; P;</span><br><span class="line">    <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">postorderTraversal</span><span class="params">(TreeNode* root)</span> </span>&#123;</span><br><span class="line">        stack&lt;P&gt; st;</span><br><span class="line">        st.<span class="built_in">push</span>(&#123;root,<span class="literal">false</span>&#125;);</span><br><span class="line">        <span class="keyword">while</span>(!st.<span class="built_in">empty</span>())&#123;</span><br><span class="line">            P p = st.<span class="built_in">top</span>();</span><br><span class="line">            st.<span class="built_in">pop</span>();</span><br><span class="line">            <span class="keyword">if</span>(!p.first) <span class="keyword">continue</span>;</span><br><span class="line">            <span class="keyword">if</span>(p.second)&#123;</span><br><span class="line">                ans.<span class="built_in">push_back</span>(p.first-&gt;val);</span><br><span class="line">            &#125;<span class="keyword">else</span>&#123;</span><br><span class="line">                p.second = <span class="literal">true</span>;</span><br><span class="line">                st.<span class="built_in">push</span>(p);</span><br><span class="line">                st.<span class="built_in">push</span>(&#123;p.first-&gt;right,<span class="literal">false</span>&#125;);</span><br><span class="line">                st.<span class="built_in">push</span>(&#123;p.first-&gt;left,<span class="literal">false</span>&#125;);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// </span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    vector&lt;<span class="type">int</span>&gt; ans;</span><br><span class="line">    <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">postorderTraversal</span><span class="params">(TreeNode* root)</span> </span>&#123;</span><br><span class="line">        stack&lt;TreeNode*&gt; st;</span><br><span class="line">        <span class="keyword">if</span>(root) st.<span class="built_in">push</span>(root);</span><br><span class="line">        TreeNode* pre =  <span class="literal">nullptr</span>;</span><br><span class="line">        <span class="keyword">while</span>(!st.<span class="built_in">empty</span>())&#123;</span><br><span class="line">            <span class="keyword">while</span>(st.<span class="built_in">top</span>()-&gt;left || st.<span class="built_in">top</span>()-&gt;right)&#123;</span><br><span class="line">                TreeNode* node = st.<span class="built_in">top</span>();</span><br><span class="line">                <span class="comment">// 如果左右子树已经访问过了,直接跳出,不要在入栈</span></span><br><span class="line">                <span class="keyword">if</span>(node-&gt;right)&#123;</span><br><span class="line">                    <span class="keyword">if</span>(node-&gt;right == pre) <span class="keyword">break</span>;</span><br><span class="line">                    st.<span class="built_in">push</span>(node-&gt;right);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span>(node-&gt;left)&#123;</span><br><span class="line">                    <span class="keyword">if</span>(node-&gt;left == pre) <span class="keyword">break</span>;</span><br><span class="line">                    st.<span class="built_in">push</span>(node-&gt;left);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            ans.<span class="built_in">push_back</span>(st.<span class="built_in">top</span>()-&gt;val);</span><br><span class="line">            pre = st.<span class="built_in">top</span>();</span><br><span class="line">            st.<span class="built_in">pop</span>();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>



<h2 id="Morris遍历"><a href="#Morris遍历" class="headerlink" title="Morris遍历"></a>Morris遍历</h2><p><del>挖个坑，以后填吧</del></p>
<h1 id="从中序遍历和前-后-序遍历构造二叉树"><a href="#从中序遍历和前-后-序遍历构造二叉树" class="headerlink" title="从中序遍历和前(后)序遍历构造二叉树"></a>从中序遍历和前(后)序遍历构造二叉树</h1><ul>
<li>注意：这种构造方法，首先必须要求有<strong>中序遍历</strong>（否则无法划分左右边界）。</li>
<li>其次，必须保证个元素的唯一性，否则构造出的二叉树不唯一。</li>
</ul>
<p><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/">105. 从前序与中序遍历序列构造二叉树</a></p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function">TreeNode* <span class="title">buildTree</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; preorder, vector&lt;<span class="type">int</span>&gt;&amp; inorder)</span> </span>&#123;</span><br><span class="line">        <span class="type">int</span> n = preorder.<span class="built_in">size</span>();</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">create</span>(<span class="number">0</span>,n<span class="number">-1</span>,<span class="number">0</span>,n<span class="number">-1</span>,preorder,inorder);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function">TreeNode* <span class="title">create</span><span class="params">(<span class="type">int</span> preL,<span class="type">int</span> preR,<span class="type">int</span> inL,<span class="type">int</span> inR,vector&lt;<span class="type">int</span>&gt;&amp; preorder, vector&lt;<span class="type">int</span>&gt;&amp; inorder)</span></span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(preL&gt;preR)&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">nullptr</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span> val  = preorder[preL];</span><br><span class="line">        TreeNode* node = <span class="keyword">new</span> <span class="built_in">TreeNode</span>(val);</span><br><span class="line">        <span class="type">int</span> idx = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i=inL;i&lt;=inR;i++)&#123;</span><br><span class="line">            <span class="keyword">if</span>(inorder[i]==val)&#123;</span><br><span class="line">                idx = i;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span> numLeft = idx-inL;</span><br><span class="line">        node-&gt;left = <span class="built_in">create</span>(preL+<span class="number">1</span>,preL+numLeft,inL,idx<span class="number">-1</span>,preorder,inorder);</span><br><span class="line">        node-&gt;right = <span class="built_in">create</span>(preL+numLeft+<span class="number">1</span>,preR,idx+<span class="number">1</span>,inR,preorder,inorder);</span><br><span class="line">        <span class="keyword">return</span> node;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>


<p><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal/">106. 从中序与后序遍历序列构造二叉树</a></p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function">TreeNode* <span class="title">buildTree</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; inorder, vector&lt;<span class="type">int</span>&gt;&amp; postorder)</span> </span>&#123;</span><br><span class="line">        <span class="type">int</span> n = inorder.<span class="built_in">size</span>();</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">create</span>(<span class="number">0</span>,n<span class="number">-1</span>,<span class="number">0</span>,n<span class="number">-1</span>,inorder,postorder);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function">TreeNode* <span class="title">create</span><span class="params">(<span class="type">int</span> postL,<span class="type">int</span> postR,<span class="type">int</span> inL,<span class="type">int</span> inR,vector&lt;<span class="type">int</span>&gt;&amp; inorder, vector&lt;<span class="type">int</span>&gt;&amp; postorder)</span></span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(postL&gt;postR)&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">nullptr</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        TreeNode* root = <span class="keyword">new</span> <span class="built_in">TreeNode</span>(postorder[postR]); </span><br><span class="line">        <span class="type">int</span> idx = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i = inL;i&lt;=inR;i++)&#123;</span><br><span class="line">            <span class="keyword">if</span>(inorder[i]==postorder[postR])&#123;</span><br><span class="line">                idx = i;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span> numLeft = idx-inL;</span><br><span class="line">        root-&gt;left = <span class="built_in">create</span>(postL,postL+numLeft<span class="number">-1</span>,inL,idx<span class="number">-1</span>,inorder,postorder);</span><br><span class="line">        root-&gt;right = <span class="built_in">create</span>(postL+numLeft,postR<span class="number">-1</span>,idx+<span class="number">1</span>,inR,inorder,postorder);</span><br><span class="line">        <span class="keyword">return</span> root;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>


<ul>
<li><p><a target="_blank" rel="noopener" href="https://ac.nowcoder.com/acm/contest/6781/A">先序遍历与后序遍历序列，求其中序遍历序列</a></p>
<ul>
<li>题意：<br>  给定一棵有n个结点的二叉树的先序遍历与后序遍历序列，求其中序遍历序列。<br>  若某节点只有一个子结点，则此处将其看作左儿子结点<br>  （题目来自牛客网）</li>
</ul>
</li>
</ul>
<p>做法也是一样的，找出根节点，然后根据规则，划分左右子树。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">private</span>:</span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">Node</span>&#123;</span><br><span class="line">        <span class="type">int</span> val;</span><br><span class="line">        Node* left= <span class="literal">nullptr</span>,*right= <span class="literal">nullptr</span>;</span><br><span class="line">        <span class="built_in">Node</span>(<span class="type">int</span> val):<span class="built_in">val</span>(val)&#123;&#125;</span><br><span class="line">    &#125;;</span><br><span class="line">    Node* root;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 返回所求中序序列</span></span><br><span class="line"><span class="comment">     * @param n int整型 二叉树节点数量</span></span><br><span class="line"><span class="comment">     * @param pre int整型vector 前序序列</span></span><br><span class="line"><span class="comment">     * @param suf int整型vector 后序序列</span></span><br><span class="line"><span class="comment">     * @return int整型vector</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">inOrder</span><span class="params">(Node* p)</span></span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(p== <span class="literal">nullptr</span>) <span class="keyword">return</span>;</span><br><span class="line">        <span class="built_in">inOrder</span>(p-&gt;left);</span><br><span class="line">        inOrderList.<span class="built_in">push_back</span>(p-&gt;val);</span><br><span class="line">        <span class="built_in">inOrder</span>(p-&gt;right);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function">Node* <span class="title">create</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; pre,<span class="type">int</span> pl,<span class="type">int</span> pr, vector&lt;<span class="type">int</span>&gt;&amp; suf,<span class="type">int</span> sl,<span class="type">int</span> sr)</span></span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(pl&gt;pr) <span class="keyword">return</span> <span class="literal">nullptr</span>;</span><br><span class="line">        Node* p = <span class="keyword">new</span> <span class="built_in">Node</span>(pre[pl]);</span><br><span class="line">        <span class="keyword">if</span>(pl&lt;pr)&#123;</span><br><span class="line">            <span class="type">int</span> idx = <span class="number">-1</span>;</span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> i=sl;i&lt;=sr;i++)&#123;</span><br><span class="line">                <span class="keyword">if</span>(suf[i] == pre[pl+<span class="number">1</span>])&#123;</span><br><span class="line">                    idx = i;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="type">int</span> lenOfLeft = idx-sl+<span class="number">1</span>;</span><br><span class="line">            p-&gt;left = <span class="built_in">create</span>(pre,pl+<span class="number">1</span>,pl+lenOfLeft,suf,sl,sl+lenOfLeft<span class="number">-1</span>);</span><br><span class="line">            p-&gt;right = <span class="built_in">create</span>(pre,pl+lenOfLeft+<span class="number">1</span>,pr,suf,sl+lenOfLeft,sr<span class="number">-1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> p;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    vector&lt;<span class="type">int</span>&gt; inOrderList;</span><br><span class="line">    <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">solve</span><span class="params">(<span class="type">int</span> n, vector&lt;<span class="type">int</span>&gt;&amp; pre, vector&lt;<span class="type">int</span>&gt;&amp; suf)</span> </span>&#123;</span><br><span class="line">        root = <span class="built_in">create</span>(pre,<span class="number">0</span>,n<span class="number">-1</span>,suf,<span class="number">0</span>,n<span class="number">-1</span>);</span><br><span class="line">        <span class="built_in">inOrder</span>(root);</span><br><span class="line">        <span class="keyword">return</span> inOrderList;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br></pre></td></tr></table></figure>


<h1 id="从中序遍历和前-后-序遍历构造二叉树-1"><a href="#从中序遍历和前-后-序遍历构造二叉树-1" class="headerlink" title="从中序遍历和前(后)序遍历构造二叉树"></a>从中序遍历和前(后)序遍历构造二叉树</h1><blockquote>
<p>暂时没找到相关题目。<br>大致思路是，根据层次遍历的有序性</p>
</blockquote>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/yw2018/article/details/105880159?utm_medium=distribute.pc_relevant_t0.none-task-blog-searchFromBaidu-1.control&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-searchFromBaidu-1.control">别人的博客</a></p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/11/02/MongoDB%20%E7%B4%A2%E5%BC%95/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/11/02/MongoDB%20%E7%B4%A2%E5%BC%95/" class="post-title-link" itemprop="url">MongoDB 索引</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>
      

      <time title="创建时间：2021-11-02 08:08:21 / 修改时间：08:41:33" itemprop="dateCreated datePublished" datetime="2021-11-02T08:08:21+08:00">2021-11-02</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/NoSQL/" itemprop="url" rel="index"><span itemprop="name">NoSQL</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <p>索引支持在 MongoDB 中高效执行查询。</p>
<p><strong>如果没有索引，MongoDB 必须执行<em>集合扫描</em>，即扫描<em>集合中的</em>每个文档，以选择那些与查询语句匹配的文档</strong>。如果查询存在合适的索引，MongoDB 可以使用该索引来限制它必须检查的文档数量。</p>
<p>索引是特殊的数据结构（<strong>MongoDB 索引使用 B 树数据结构</strong>），它以易于遍历的形式存储集合数据集的一小部分。索引存储特定字段或字段集的值，按字段值排序。索引条目的排序支持高效的等式匹配和基于范围的查询操作。此外，MongoDB 可以使用索引中的排序返回排序结果。</p>
<p>从根本上说，<strong>MongoDB 中的索引类似于其他数据库系统中的索引</strong>（因为数据结构使用的都是B树）。MongoDB 在<strong>集合</strong>级别定义索引，并支持 MongoDB 集合中文档的任何字段或子字段的索引。</p>
<h2 id="默认-id索引"><a href="#默认-id索引" class="headerlink" title="默认_id索引"></a>默认<code>_id</code>索引</h2><p>MongoDB在创建集合期间在<code>_id</code>字段上 创建唯一索引。该索引可防止客户端插入具有相同字段值的两个文档。您不能在字段上删除此索引。</p>
<h2 id="创建索引"><a href="#创建索引" class="headerlink" title="创建索引"></a>创建索引</h2><p>索引一旦创建就不能重命名。相反，您必须删除并使用新名称重新创建索引。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">db.collection.createIndex( &lt;key and index type specification&gt;, &lt;options&gt; )</span><br></pre></td></tr></table></figure>

<h2 id="查看索引"><a href="#查看索引" class="headerlink" title="查看索引"></a>查看索引</h2><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">db.collection.getIndexes()</span><br></pre></td></tr></table></figure>




<h2 id="索引类型"><a href="#索引类型" class="headerlink" title="索引类型"></a>索引类型</h2><p>MongoDB 提供了许多不同的索引类型来支持特定类型的数据和查询。</p>
<h3 id="单字段索引"><a href="#单字段索引" class="headerlink" title="单字段索引"></a>单字段索引</h3><p>MongoDB支持在文档的单个字段上创建用户定义的升序&#x2F;降序索引。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">db.collection.createIndex( &#123; score: 1 &#125; )</span><br></pre></td></tr></table></figure>



<p><img src="https://docs.mongodb.com/manual/images/index-ascending.bakedsvg.svg"></p>
<h3 id="复合索引"><a href="#复合索引" class="headerlink" title="复合索引"></a>复合索引</h3><p>MongoDB 还支持多个字段上的用户定义索引，即 <a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/core/index-compound/">复合索引</a>。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">db.collection.createIndex( &#123; userid: 1, score: -1 &#125; )</span><br></pre></td></tr></table></figure>



<p>复合索引中列出的字段顺序很重要。例如，如果复合索引由 组成<code>&#123; userid: 1, score: -1 &#125;</code>，则索引首先按升序排序<code>userid</code>，然后在每个<code>userid</code> 值中按降序排序<code>score</code>。</p>
<p><img src="https://docs.mongodb.com/manual/images/index-compound-key.bakedsvg.svg"></p>
<h3 id="多键索引"><a href="#多键索引" class="headerlink" title="多键索引"></a>多键索引</h3><p>MongoDB 使用<a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/core/index-multikey/">多键索引</a>来索引<strong>存储在数组中的内容</strong>。<br>如果你索引一个包含数组值的字段，MongoDB 会<strong>为数组的每个元素创建单独的索引条目</strong>。这些<a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/core/index-multikey/">多键索引</a>允许查询通过匹配数组的一个或多个元素来选择包含数组的文档。</p>
<p>如果索引字段包含数组值，MongoDB 会自动判断是否创建多键索引；您<strong>不需要显式指定多键类型</strong>。</p>
<p><img src="https://docs.mongodb.com/manual/images/index-multikey.bakedsvg.svg"></p>
<h3 id="哈希索引"><a href="#哈希索引" class="headerlink" title="哈希索引"></a>哈希索引</h3><p>为了支持<a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/core/hashed-sharding/#std-label-sharding-hashed-sharding">基于散列的分片</a>，MongoDB 提供了一种<a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/core/index-hashed/">散列索引</a>类型，它对字段值的散列值进行索引。这些索引在其范围内具有更随机的值分布，但<strong>仅支持相等匹配</strong>，<strong>不支持基于范围的查询</strong>。</p>
<blockquote>
<p><a target="_blank" rel="noopener" href="https://docs.mongodb.com/manual/indexes/">官方文档索引</a></p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/10/31/MySQL%E7%B3%BB%E5%88%97%E2%80%94%E2%80%94-InnoDB%E7%9A%84MVCC%E6%9C%BA%E5%88%B6/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/10/31/MySQL%E7%B3%BB%E5%88%97%E2%80%94%E2%80%94-InnoDB%E7%9A%84MVCC%E6%9C%BA%E5%88%B6/" class="post-title-link" itemprop="url">MySQL系列—— InnoDB的MVCC机制</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-10-31 23:37:11" itemprop="dateCreated datePublished" datetime="2021-10-31T23:37:11+08:00">2021-10-31</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2022-09-15 15:27:06" itemprop="dateModified" datetime="2022-09-15T15:27:06+08:00">2022-09-15</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/MySQL/" itemprop="url" rel="index"><span itemprop="name">MySQL</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="前置知识"><a href="#前置知识" class="headerlink" title="前置知识"></a>前置知识</h1><h2 id="事务并发执行时遇到的一致性问题"><a href="#事务并发执行时遇到的一致性问题" class="headerlink" title="事务并发执行时遇到的一致性问题"></a>事务并发执行时遇到的一致性问题</h2><ul>
<li>脏写</li>
</ul>
<p>一个事务修改了另一个未提交事务修改过的数据。</p>
<ul>
<li>脏读</li>
</ul>
<p>一个事务读到了另一个未提交事务修改过的数据。</p>
<ul>
<li>不可重复读</li>
</ul>
<p>一个事务多次读到的同一数据行的值不相同。</p>
<ul>
<li>幻读</li>
</ul>
<p>一个事务多次按照同一个搜索条件，读到了不同的数据行。</p>
<h2 id="隔离级别"><a href="#隔离级别" class="headerlink" title="隔离级别"></a>隔离级别</h2><p>隔离级别描述了并发事务执行时对竞争数据的互相影响的程度。</p>
<p>最理想化的隔离应该每个事务执行时互不影响，互不干涉，就像数据库此时只为它一个事务使用似的。</p>
<p>不过，一般使用场景下，我们会<strong>舍弃一定的隔离性去换取一部分性能</strong>,</p>
<p>于是SQL标准设立的4个隔离级别，隔离级别越低，越有可能出现数据不一致的情况，不过并发性能也越高。</p>
<blockquote>
<p>注意，这是SQL指定的标准，不是MySQL指定的。</p>
<p>不同DBMS对隔离级别的支持也不一样。</p>
</blockquote>
<table>
<thead>
<tr>
<th>Isolation Level P1（隔离级别）</th>
<th>脏读</th>
<th>不可重复读</th>
<th>幻读</th>
</tr>
</thead>
<tbody><tr>
<td>READ UNCOMMITTED （读未提交）</td>
<td>Possible</td>
<td>Possible</td>
<td>Possible</td>
</tr>
<tr>
<td>READ COMMITTED（读已提交）</td>
<td>Not Possible</td>
<td>Possible</td>
<td>Possible</td>
</tr>
<tr>
<td>REPEATABLE READ （可重复读）</td>
<td>Not Possible</td>
<td>Not Possible</td>
<td>Possible</td>
</tr>
<tr>
<td>SERIALIZABLE  （可序列化）</td>
<td>Not Possible</td>
<td>Not Possible</td>
<td>Not Possible</td>
</tr>
</tbody></table>
<p>在任何隔离级别下，脏写都是不可接受的，这是一个对一致性影响极大的操作。</p>
<p>MySQL会用锁去保证不会有两个并发事务同时修改一条记录。</p>
<p><strong>MySQL默认使用的隔离级别是REPEATABLE READ</strong>，而且MySQL的可重复读，可以在很大程度上禁止幻读现象的发生，但不能绝对禁止（后文会给出原因）。</p>
<h1 id="MVCC"><a href="#MVCC" class="headerlink" title="MVCC"></a>MVCC</h1><p>Multi-Version Concurrency Control（多版本并发控制），MySQL中如何实现？</p>
<p>对于<code>READ UNCOMMITTED </code>直接读取B+树的最新记录就好了，无需任何多余操作，很简单；</p>
<p>对于<code>SERIALIZABLE</code>是通过对select语句加共享锁的方式实现的，后面会详细将锁的实现与使用；</p>
<p>对于<code>READ COMMITTED</code>和<code>REPEATABLE READ</code>的实现是比较复杂的，具体看下面的<strong>版本链+ReadView</strong>。</p>
<h2 id="版本链"><a href="#版本链" class="headerlink" title="版本链"></a>版本链</h2><ul>
<li>trx_id</li>
</ul>
<p>一个事务每次对某条聚簇索引进行更改的时候，都会把该事务的事务id赋值给trx_id列。</p>
<ul>
<li>roll_pointer</li>
</ul>
<p>每次对某条聚簇索引记录进行改动时，都会把旧的记录写入到undo日志中，这个隐藏列就相当于一个指针，可以通过它找到修改前的信息。</p>
<p><img src="/2021/10/31/MySQL%E7%B3%BB%E5%88%97%E2%80%94%E2%80%94-InnoDB%E7%9A%84MVCC%E6%9C%BA%E5%88%B6/mvcc.png"></p>
<p>如上图，每一次修改记录，就会产生一条undo日志（当然在具体实现的时候是许多条undo log）,每个undo日志也有一个 roll_pointer，那么对于某一数据行，B+树中的行记录和undo日志就构成一条版本链。</p>
<p>这条版本链在MVCC机制中起到重要作用。</p>
<h2 id="ReadView（一致性视图）"><a href="#ReadView（一致性视图）" class="headerlink" title="ReadView（一致性视图）"></a>ReadView（一致性视图）</h2><p><code>READ COMMITTED</code>和<code>REPEATABLE READ</code>二者的一个共同的问题就是，如何判断版本链中的哪个版本是当前事务可见的。</p>
<p>MySQL设计出ReadView这样的数据结构解决这个问题。</p>
<p>它包括4个比较重要的概念：</p>
<ol>
<li><strong>m_ids</strong> : 生成此ReadView时，当前系统<strong>活跃的</strong>读写事务的事务id列表；</li>
<li><strong>min_trx_id</strong> ：生成此ReadView时，当前系统活跃的读写事务列表中最小的事务id，也就是m_ids中的最小值；</li>
<li><strong>max_trx_id</strong> ：生成此ReadView时，系统应该分配给下一个事务的事务id值；</li>
<li><strong>creator_trx_id</strong> ：生成此ReadView的事务id。</li>
</ol>
<p>有了这个ReadView，在访问某条记录时，采用下面的方式判断即可。</p>
<ol>
<li>如果被访问版本的<strong>trx_id和creator_trx_id相同</strong>，说明这个事务正在访问它自己修改过的记录，所以可见。</li>
<li>如果被访问版本的<strong>trx_id小于min_trx_id</strong>，说明生成此版本的事务在当前事务生成readview之前就已经提交了，自然也是可见的。</li>
<li>如果被访问版本的<strong>trx_id大于等于max_trx_id</strong>，表明生成此版本的事务在当前事务后才开启，所以不可见。</li>
<li>如果被访问版本的<strong>trx_id在min_trx_id-max_trx_id之间</strong>，那么需要判断trx_id是否在m_ids之间。如果在，说明此事务是活跃的，记录不可以被访问；如果不在，说明生成此已经结束，记录可见。</li>
<li>如果某个版本的记录对当前事务不可见，那么就顺着版本链往下找，并按照上面的方式判断可见性，直到遍历到最后一个版本返回空。</li>
</ol>
<p>那么具体怎么实现<code>READ COMMITTED</code>和<code>REPEATABLE READ</code>呢？</p>
<p>实际上二者的区别就在于生成ReadView的时机不同。</p>
<p><code>READ COMMITTED</code>在每次读取一条记录前都去生成ReadView，</p>
<p><code>REPEATABLE READ</code>则在第一次读取数据的时候生成ReadView（不过可以使用<code>WITH CONSISTENT SANPSHOT</code>指定一开启事务就生成ReadView）。</p>
<h2 id="二级索引和MVCC"><a href="#二级索引和MVCC" class="headerlink" title="二级索引和MVCC"></a>二级索引和MVCC</h2><p>注意只有聚簇索引记录才有 trx_id和roll_pointer。那么如果使用二级索引查询记录如何判断可见性呢？</p>
<ol>
<li>二级索引页面的Page Header部分有一个PAGE_MAX_TRX_ID的属性，它代表着<strong>修改</strong>该二级索引页面的最大事务id。如果此<code>PAGE_MAX_TRX_ID</code>是小于min_trx_id的，说明整个页面对当前事务都是可见的，判断完毕。</li>
<li>否则， 根据二级索引的主键值执行回表操作，得到对应的聚簇索引记录根据前面的方法判断可见性，然后对第一个可见的记录判断是否和利用二级索引查询的索引列的值相匹配，匹配，则此条记录可见；否则继续往下找，<strong>直到聚簇索引记录可见同时二级索引匹配条件满足</strong>。</li>
</ol>
<blockquote>
<p><a target="_blank" rel="noopener" href="https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/tr-95-51.pdf">A Critique of ANSI SQL Isolation Levels</a></p>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/hhc9_9/article/details/109394221">Mysql8.0下验证mysql的四种隔离级别</a></p>
<p>《MySQL是怎样运行的》</p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/10/31/Redis%E7%B3%BB%E5%88%97%E7%AC%94%E8%AE%B0%E4%B9%8B-%E2%80%94%E2%80%94-%E7%BC%93%E5%AD%98%E4%B8%89%E5%A4%A7%E9%97%AE%E9%A2%98%E5%8F%8A%E5%85%B6%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/10/31/Redis%E7%B3%BB%E5%88%97%E7%AC%94%E8%AE%B0%E4%B9%8B-%E2%80%94%E2%80%94-%E7%BC%93%E5%AD%98%E4%B8%89%E5%A4%A7%E9%97%AE%E9%A2%98%E5%8F%8A%E5%85%B6%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88/" class="post-title-link" itemprop="url">Redis系列笔记之 —— 缓存三大问题及其解决方案</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-10-31 00:00:00" itemprop="dateCreated datePublished" datetime="2021-10-31T00:00:00+08:00">2021-10-31</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2021-11-29 12:55:54" itemprop="dateModified" datetime="2021-11-29T12:55:54+08:00">2021-11-29</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/Redis/" itemprop="url" rel="index"><span itemprop="name">Redis</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <blockquote>
<p>老八股文了，redis必问问题。</p>
</blockquote>
<h1 id="缓存的利与弊"><a href="#缓存的利与弊" class="headerlink" title="缓存的利与弊"></a>缓存的利与弊</h1><h2 id="引入缓存的优点"><a href="#引入缓存的优点" class="headerlink" title="引入缓存的优点"></a>引入缓存的优点</h2><ul>
<li>能够缩短服务的响应时间，给用户带来更好的体验。</li>
<li>能够增大系统的吞吐量，依然能够提升用户体验。</li>
<li>减轻数据库的压力，防止高峰期数据库被压垮，导致整个线上服务 BOOM！</li>
</ul>
<h2 id="引入缓存的缺点"><a href="#引入缓存的缺点" class="headerlink" title="引入缓存的缺点"></a>引入缓存的缺点</h2><ul>
<li>缓存有多种选型，是内存缓存，memcached 还是 redis，你是否都熟悉，如果不熟悉，无疑增加了维护的难度（本来是个纯洁的数据库系统）。</li>
<li>缓存系统也要考虑分布式，比如 redis 的分布式缓存还会有很多坑，无疑增加了系统的复杂性。</li>
<li>在特殊场景下，如果对缓存的准确性有非常高的要求，就必须考虑缓存和数据库的一致性问题。</li>
</ul>
<p>适合缓存的数据：读多写少、不常变化的数据，比如详情，评论等。</p>
<p>不适合缓存的数据：对于那些经常变化的数据，其实并不适合缓存，一方面会增加系统的复杂性（缓存的更新，缓存脏数据），另一方面也给系统带来一定的不稳定性（缓存系统的维护）。</p>
<p>但一些极端情况下，你需要将一些会变动的数据进行缓存，比如想要页面显示准实时的库存数，或者其他一些特殊业务场景。这时候你需要保证缓存不能（一直）有脏数据，这就需要再深入讨论一下，也就是<strong>缓存-数据库一致性的问题</strong>。</p>
<h1 id="缓存穿透"><a href="#缓存穿透" class="headerlink" title="缓存穿透"></a>缓存穿透</h1><h2 id="问题描述"><a href="#问题描述" class="headerlink" title="问题描述"></a>问题描述</h2><p>缓存穿透是指<strong>缓存和数据库中都没有的数据</strong>，而用户不断发起请求，如发起id为-1的数据或者特别大的不存在的数据。有可能是黑客利用漏洞攻击从而去压垮应用的数据库。</p>
<h2 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h2><p>对于缓存穿透问题，常见的解决方案有以下三种：</p>
<ol>
<li><strong>验证拦截</strong>：接口层进行校验，如鉴定用户权限，对ID之类的字段做基础的校验，如<code>id&lt;=0</code>的字段直接拦截；</li>
<li><strong>缓存空数据</strong>：当数据库查询到的数据为空时，也将这条数据进行缓存，但缓存的有效性设置得要较短，以免影响正常数据的缓存（因为有可能一开始没有这个key，但后来有了，当然你可以主动删除缓存）；</li>
<li>使用布隆过滤器：布隆过滤器是一种比较独特数据结构，有一定的误差。当它指定一个数据存在时，它可能存在也可能不存在，但是当它指定一个数据不存在时，那么它一定是不存在的。（<del>具体使用方法参考文章模链接</del>）</li>
</ol>
<h1 id="缓存击穿"><a href="#缓存击穿" class="headerlink" title="缓存击穿"></a>缓存击穿</h1><h2 id="问题描述-1"><a href="#问题描述-1" class="headerlink" title="问题描述"></a>问题描述</h2><p>缓存击穿是指当前热点数据存储到期时，<strong>多个线程同时并发访问热点数据</strong>。因为缓存刚过期，<strong>所有并发请求都会到数据库中查询数据</strong>。</p>
<h2 id="解决方案-1"><a href="#解决方案-1" class="headerlink" title="解决方案"></a>解决方案</h2><ul>
<li><p>将热点数据设置为永不过期；</p>
</li>
<li><p>加互斥锁：互斥锁可以控制查询数据库的线程访问，只让一个请求通过，只有一个请求去数据库拉取数据，取完数据，不管如何都需要释放锁。</p>
</li>
<li><p>添加超时标记：在缓存的对象上增加一个属性来标识超时时间，当获取到数据后，校验数据内部的标记时间，判定是否快超时了，如果是，异步发起一个线程（控制好并发）去主动更新该缓存。</p>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> String <span class="title function_">get</span><span class="params">(Object key, <span class="type">int</span> retryCount)</span> &#123;</span><br><span class="line">    <span class="comment">// 重试次数太多直接 return null</span></span><br><span class="line">    <span class="keyword">if</span>(retryCount &gt; <span class="number">5</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">String</span> <span class="variable">value</span> <span class="operator">=</span> redis.get(key);</span><br><span class="line">    <span class="keyword">if</span> (value == <span class="literal">null</span>) &#123; <span class="comment">// 代表缓存值过期</span></span><br><span class="line">        <span class="comment">// 设置1 min的超时，防止del操作失败的时候，下次缓存过期一直不能load db</span></span><br><span class="line">        <span class="keyword">if</span> (redis.setnx(key_mutex, <span class="number">1</span>, <span class="number">60</span>) == <span class="number">1</span>) &#123;  <span class="comment">// 代表设置成功</span></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                value = db.get(key);</span><br><span class="line">                redis.set(key, value, expire_secs);</span><br><span class="line">            &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                <span class="comment">// 确保最后删除，释放锁</span></span><br><span class="line">                redis.del(key_mutex); </span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// 这个时候代表同时候的其他线程已经load db并回设到缓存了，这时候重试获取缓存值即可</span></span><br><span class="line">            sleep(<span class="number">200</span>);</span><br><span class="line">            get(key, retryCount + <span class="number">1</span>);  <span class="comment">// 重试</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> value;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h1 id="缓存雪崩"><a href="#缓存雪崩" class="headerlink" title="缓存雪崩"></a>缓存雪崩</h1><h2 id="问题描述-2"><a href="#问题描述-2" class="headerlink" title="问题描述"></a>问题描述</h2><p>缓存雪崩是指缓存中有大量的数据，在同一个时间点，或者较短的时间段内，全部过期了，这个时候请求过来，缓存没有数据，都会请求数据库，则数据库的压力就会突增，扛不住就会宕机。</p>
<p>造成缓存雪崩的关键在于在同一时间大规模的key失效。为什么会出现这个问题呢，有几种可能，</p>
<p>第一种可能是Redis宕机，</p>
<p>第二种可能是采用了相同的过期时间。</p>
<h2 id="解决方案-2"><a href="#解决方案-2" class="headerlink" title="解决方案"></a>解决方案</h2><ol>
<li>如果是热点数据，那么可以考虑<strong>设置永远不过期</strong>。</li>
<li>缓存的过期时间除非比较严格，要不考虑<strong>设置一个波动随机值</strong>，比如理论十分钟，那这类key的缓存时间都加上一个1-3分钟，过期时间在7-13分钟内波动，<strong>有效防止都在同一个时间点上大量过期</strong>。</li>
<li>如果所有的热点数据在一台redis服务器上，也是极其危险的，如果网络有问题，或者redis服务器挂了，那么所有的热点数据也会雪崩（查询不到），因此<strong>将热点数据打散分不到不同的机器上</strong>，也可以有效减少这种情况。</li>
<li>对于redis服务挂掉的问题,可以实现redis的高可用主从架构, 并且做redis的持久化, 在redis挂掉的同时时读取本地缓存数据, 同时恢复redis服务加载持久化的数据。</li>
</ol>
<ul>
<li>【事前】<strong>高可用缓存</strong>：高可用缓存是防止出现整个缓存故障。即使个别节点，机器甚至机房都关闭，系统仍然可以提供服务，Redis 哨兵(Sentinel) 和 Redis 集群(Cluster) 都可以做到高可用；</li>
<li>【事中】<strong>缓存降级</strong>（临时支持）：当访问次数急剧增加导致服务出现问题时，我们如何确保服务仍然可用。在国内使用比较多的是 Hystrix，它通过熔断、降级、限流三个手段来降低雪崩发生后的损失。只要确保数据库不死，系统总可以响应请求，每年的春节 12306 我们不都是这么过来的吗？只要还可以响应起码还有抢到票的机会；</li>
<li>【事后】<strong>备份和快速预热</strong>：Redis数据备份和恢复、快速缓存预热。</li>
</ul>
<blockquote>
<p>参考链接：</p>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/Revivedsun/article/details/94992323">Guava布隆过滤器(boomfilter)使用简介</a></p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




  <nav class="pagination">
    <a class="extend prev" rel="prev" title="上一页" aria-label="上一页" href="/page/19/"><i class="fa fa-angle-left"></i></a><a class="page-number" href="/">1</a><span class="space">&hellip;</span><a class="page-number" href="/page/19/">19</a><span class="page-number current">20</span><a class="page-number" href="/page/21/">21</a><span class="space">&hellip;</span><a class="page-number" href="/page/26/">26</a><a class="extend next" rel="next" title="下一页" aria-label="下一页" href="/page/21/"><i class="fa fa-angle-right"></i></a>
  </nav>

</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">


<div class="copyright">
  &copy; 
  <span itemprop="copyrightYear">2023</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">SongyangJi</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.js.org/muse/" rel="noopener" target="_blank">NexT.Muse</a> 强力驱动
  </div>

    </div>
  </footer>

  
  <div class="toggle sidebar-toggle" role="button">
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
  </div>
  <div class="sidebar-dimmer"></div>
  <div class="back-to-top" role="button" aria-label="返回顶部">
    <i class="fa fa-arrow-up fa-lg"></i>
    <span>0%</span>
  </div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js" integrity="sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=" crossorigin="anonymous"></script>
<script src="/js/comments.js"></script><script src="/js/utils.js"></script><script src="/js/motion.js"></script><script src="/js/schemes/muse.js"></script><script src="/js/next-boot.js"></script>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/hexo-generator-searchdb/1.4.1/search.js" integrity="sha256-1kfA5uHPf65M5cphT2dvymhkuyHPQp5A53EGZOnOLmc=" crossorigin="anonymous"></script>
<script src="/js/third-party/search/local-search.js"></script>





  





</body>
</html>
